// Created with MonoDevelop
//
//    Copyright (C) 2009 Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.



using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using PDFjet.NET;
using System.IO;

using Hiperscan.Unix;


namespace Hiperscan.PdfLib
{
	public static class Pdf
	{
		public static void OpenValidationDocumentation(string fname, string link)
		{
			Console.WriteLine("Open validation docu {0}-{1}", fname, link);
			Pdf.Open(fname, link);
		}
		
		public static void Open(string fname, string destination)
		{
			string file = System.IO.Path.GetFullPath(fname);
			if (string.IsNullOrEmpty(destination))
			{
                System.Diagnostics.Process.Start(file);
				return;
			}

			file = "\"" + file + "\"";

			if (Environment.OSVersion.Platform == PlatformID.Unix)
			{
				if (File.Exists("/usr/bin/evince"))
                    System.Diagnostics.Process.Start("evince", file + " -n " + destination);
				else if (File.Exists("/usr/bin/acroread"))
                    System.Diagnostics.Process.Start("acroread", "/a nameddest=" + destination + " " + file);
				else
					throw new ApplicationException(Catalog.GetString("Cannot find PDF viewer."));
			}
			else
			{
				Microsoft.Win32.RegistryKey adobe = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software").OpenSubKey("Microsoft").OpenSubKey("Windows").OpenSubKey("CurrentVersion").OpenSubKey("App Paths").OpenSubKey("AcroRd32.exe");
				if (adobe == null)
					throw new ApplicationException(Catalog.GetString("Cannot find PDF viewer."));

				Console.WriteLine("Found Adobe Reader: " + adobe.GetValue("").ToString());
                System.Diagnostics.Process.Start(adobe.GetValue(string.Empty).ToString(), "/a nameddest=" + destination + " " + file);
			}
		}

		public static void Open(string fname)
		{
			Pdf.Open(fname, string.Empty);
		}
	}
	
	public class PdfRect
	{
		public double X	{get; set;}
		public double Y {get; set;}
		public double Width	{get;set;}
		public double Height{get;set;}
		
		public PdfRect(double x, double y, double width, double height)
		{
			this.X = x;
			this.Y = y;
			this.Height = height;
			this.Width = width;
		}
		
		public PdfRect() : this(0, 0, 0, 0)
		{
		}
	}
	
	public class PdfPoint 
	{
		public double X	{get; set;}
		public double Y {get; set;}

		
		public PdfPoint(double x, double y)
		{
			this.X = x;
			this.Y = y;
		}
	}
	
	public static class PdfColors
	{
		public readonly static int HiperscanRed = 0x00D12023;
		
		public readonly static int Black = 0x00000000;
		public readonly static int Blue = 0x000000FF;
		public readonly static int DarkGreen = 0x00008000;
		public readonly static int Gray = 0x00808080;
		public readonly static int Green = 0x0000FF00;
		public readonly static int Red = 0x00FF0000;
		public readonly static int White = 0x00FFFFFF;
	}
	
	public enum PdfTermtypes
	{
		OpeningBracket,
		ClosingBracket,
		WhiteSpace,
		NewLine,
		Tabulator,
		Word,
	}
	
	public class PdfTerm
	{
		public PdfTerm()
		{
			this.TermType = PdfTermtypes.Word;
			this.Width = 0;
			this.Content = String.Empty;
		}
		
		public PdfTermtypes TermType 	{get;set;}
		public string Content 			{get;set;}
		public double Width				{get;set;}
	}
	
	public enum PdfTextAlignments
	{
		Left,
		Right,
		Center,
		Block,
	}
	
	public enum PdfCoordinateTypes
	{
		X,
		Y,
	}
	
	public enum PdfBitmapScales
	{
		Y,
		X,
	}
		
	public enum PdfLayoutErrorTypes
	{
		None,
		XExceedance,
		XUndercut,
		YExceedance,
		YUndercut,
	}
	
	public class PdfTableStringCell
	{
		public PdfTableStringCell()
		{
			this.Value = String.Empty;
			this.FontType = PdfFontTypes.HELVETICA;
			this.FontColor = PdfColors.Black;
			this.FontSize = 10;
			this.MeasuredTextSize = new PdfRect(0,0,0,0);
			this.Align = PdfTextAlignments.Left;
		}
		
		public string Value 			{get;set;}
		public CoreFont FontType 		{get;set;}
		public int FontColor 			{get;set;}
		public double FontSize			{get;set;}
		public PdfRect MeasuredTextSize	{get;set;}
		public PdfTextAlignments Align	{get;set;}
	}
	
	public class PdfTable
	{
		private Layout Document 					{get;set;}
		private PdfTableStringCell[,] TableCells 	{get;set;}
		private double[] RowHeights					{get;set;}
		private double[] ColumnWidths				{get;set;}
		private double XPadding 					{get;set;}
		private double YPadding 					{get;set;}
		private double BorderWidth 					{get;set;}
		
		
		public PdfTable(Layout document, PdfTableStringCell[,] table_cells,double[] column_widths, double x_padding, double y_padding, double border_width)
		{
			this.Document 		= document;
			this.TableCells 	= table_cells;
			this.ColumnWidths	= column_widths;
			this.XPadding 		= x_padding;
			this.YPadding 		= y_padding;
			this.BorderWidth 	= border_width;
			
			this.RowHeights = new double[this.TableCells.GetLength(0)];
			
			for (int i = 0; i < this.TableCells.GetLength(0); i++)
			{
				this.RowHeights[i] = -1;
				for (int j = 0; j < this.TableCells.GetLength(1); j++)
				{
					double paragraph_width = this.ColumnWidths[j] - 2 * this.XPadding;
					this.Document.SetFont(this.TableCells[i,j].FontType, this.TableCells[i,j].FontSize, this.TableCells[i,j].FontColor);
					this.TableCells[i,j].MeasuredTextSize = this.Document.MeasureParagraph(this.TableCells[i,j].Value, paragraph_width);
					
					if (this.TableCells[i,j].MeasuredTextSize.Height > this.RowHeights[i])
						this.RowHeights[i] = this.TableCells[i,j].MeasuredTextSize.Height;
				}
			}
		}
		
		public double Draw(double x_pos, double y_pos, int border_color)
		{
			for (int i = 0; i < this.TableCells.GetLongLength(0); i++)
			{
				double current_x_pos = x_pos;
				for (int j = 0; j < this.TableCells.GetLongLength(1); j++)
				{
					PdfRect rect = new PdfRect();
					rect.X = current_x_pos;
					rect.Y = y_pos;
					rect.Width = this.ColumnWidths[j];
					rect.Height = this.RowHeights[i] + 2 * this.YPadding;
					this.Document.AddRectangle(rect, this.BorderWidth, border_color);
					
					
					double y_offset = this.YPadding;
					y_offset += (this.RowHeights[i] - this.TableCells[i,j].MeasuredTextSize.Height) / 2;
					
					this.Document.YPosition = y_pos + y_offset;
					this.Document.XPosition = current_x_pos + this.XPadding;
					this.Document.SetFont(this.TableCells[i,j].FontType, 
					                      this.TableCells[i,j].FontSize,
					                      this.TableCells[i,j].FontColor);
					this.Document.AddParagraph(this.TableCells[i,j].Value, 
					                           rect.Width - 2 * this.XPadding,
					                           this.TableCells[i,j].Align);
					
					current_x_pos += rect.Width - this.BorderWidth;
				}
				y_pos += this.RowHeights[i] +  2 * this.YPadding  - this.BorderWidth;
			}
			return y_pos;
		}
	}
	
	public class PdfLayoutException : Exception
	{
		public PdfLayoutErrorTypes ErrorType {get;set;}
		
		public PdfLayoutException()
		{
			this.ErrorType = PdfLayoutErrorTypes.None;
		}
		
		public PdfLayoutException(string msg) : base(msg)
		{
			this.ErrorType = PdfLayoutErrorTypes.None;
		}
		
		public PdfLayoutException(string msg, Exception inner) : base (msg, inner)
		{
			this.ErrorType = PdfLayoutErrorTypes.None;
		}
	}
	
	public static class PdfPageSizes
	{
		public readonly static float[] A3_PORTRAIT 	= PDFjet.NET.A3.PORTRAIT;
		public readonly static float[] A3_LANDSCAPE 	= PDFjet.NET.A3.LANDSCAPE;
		
		public readonly static float[] A4_PORTRAIT 	= PDFjet.NET.A4.PORTRAIT;
		public readonly static float[] A4_LANDSCAPE 	= PDFjet.NET.A4.LANDSCAPE;
		
		public readonly static float[] A5_PORTRAIT 	= PDFjet.NET.A5.PORTRAIT;
		public readonly static float[] A5_LANDSCAPE 	= PDFjet.NET.A5.LANDSCAPE;
		
		public readonly static float[] B5_PORTRAIT 	= PDFjet.NET.B5.PORTRAIT;
		public readonly static float[] B5_LANDSCAPE 	= PDFjet.NET.B5.LANDSCAPE;
		
		public readonly static float[] LETTER_PORTRAIT = PDFjet.NET.Letter.PORTRAIT;
		public readonly static float[] LETTER_LANDSCAPE = PDFjet.NET.Letter.LANDSCAPE;
	}


	public static class PdfFontTypes
	{
		public readonly static CoreFont COURIER 				= CoreFont.COURIER;
		public readonly static CoreFont COURIER_BOLD 			= CoreFont.COURIER_BOLD;
		public readonly static CoreFont COURIER_BOLD_OBLIQUE 	= CoreFont.COURIER_BOLD_OBLIQUE;
		public readonly static CoreFont COURIER_OBLIQUE 		= CoreFont.COURIER_OBLIQUE;
		
		public readonly static CoreFont HELVETICA 				= CoreFont.HELVETICA;
		public readonly static CoreFont HELVETICA_BOLD 			= CoreFont.HELVETICA_BOLD;
		public readonly static CoreFont HELVETICA_BOLD_OBLIQUE	= CoreFont.HELVETICA_BOLD_OBLIQUE;
		public readonly static CoreFont HELVETICA_OBLIQUE 		= CoreFont.HELVETICA_OBLIQUE;
		
		public readonly static CoreFont TIMES_BOLD 				= CoreFont.TIMES_BOLD;
		public readonly static CoreFont TIMES_BOLD_ITALIC 		= CoreFont.TIMES_BOLD_ITALIC;
		public readonly static CoreFont TIMES_ITALIC 			= CoreFont.TIMES_ITALIC;
		public readonly static CoreFont TIMES_ROMAN 			= CoreFont.TIMES_ROMAN;
	}

	
	
	public class Layout
	{
		public const int NO_COLOR = -1;
		
		public PDFjet.NET.PDF PDF			{get; private set;}
		public BufferedStream PdfBuffer		{get; private set;}
		
		public bool SimulateDrawing 		{get; set;}
		
		private string png_file = string.Empty;
			
		public Layout(float[] page_size, string file_name)
		{
			this.SimulateDrawing = false;
			this.PageSize = page_size;
			this.LineDistance = 2;
			
			FileStream fs = new FileStream(file_name, FileMode.Create);
			this.PdfBuffer = new BufferedStream(fs);
			
			this.PDF = new PDFjet.NET.PDF(this.PdfBuffer);
			this.LeftMargin  = 70f;
			this.RightMargin = 30f;
			this.UpperMargin = 40f;
			this.LowerMargin = 30f;
			this.AddNewPage();
		}
		
		~Layout()
		{
			try
			{
				if (!string.IsNullOrEmpty(this.png_file))
				{
					System.IO.FileInfo file = new System.IO.FileInfo(this.png_file);
					file.Delete();
				}
			}
			catch { }
		}
		
		public void Save()
		{
			this.PDF.Flush();
			this.PdfBuffer.Close();
		}
		
		
		#region Page layout
		public float[] PageSize		{get;set;}
		public Page CurrentPage 	{get;set;}
		
		public double LeftMargin 	{get;set;}
		public double RightMargin	{get;set;}
		public double UpperMargin	{get;set;}
		public double LowerMargin	{get;set;}
		public double PageWidth		{get;set;}
		public double PageHeight	{get;set;}
		
		public double InnerPageWidth
		{
			get { return this.PageWidth - this.LeftMargin - this.RightMargin; }
		}
		
		public double InnerPageHeight
		{
			get { return this.PageHeight - this.UpperMargin - this.LowerMargin; }
		}
		
		public void AddNewPage()
		{
			this.XPosition = 0;
			this.YPosition = 0;
			
			if (this.SimulateDrawing == false)
				this.CurrentPage  = new Page(this.PDF, this.PageSize);
			
			this.SetFont(PdfFontTypes.HELVETICA, 12, Color.black);
			this.PageWidth 	= this.PageSize[0];
			this.PageHeight = this.PageSize[1];
		}
		
		#endregion Page layout
		#region Positions
		
		public double LineDistance {get;set;}
		public double XPosition{get;set;}
		public double YPosition{get;set;}
		

		
		public void Feed(double feed)
		{
			this.YPosition += feed;
		}
		
		private const double Tolerance = 0.1;
		public bool CheckCoordinates(double x, double y)
		{
			if (x < -Layout.Tolerance || x > this.InnerPageWidth + Layout.Tolerance)
				return false;
			
			if (y < -Layout.Tolerance || y > this.InnerPageHeight + Layout.Tolerance)
				return false;
			
			return true;
		}
			    
		#endregion Positions
		
		#region Circle
		public bool AddCircle(PdfPoint center, double radius, double line_width, int line_color, int fill_color, bool ignore_coordinates)
		{
			if (ignore_coordinates == false)
			{
				double upper_left_x = center.X - radius;
				double upper_left_y = center.Y - radius;
				
				double lower_right_x = center.X + radius;
				double lower_right_y = center.Y + radius;

				bool can_draw = this.CheckCoordinates(upper_left_x, upper_left_y);
				if (can_draw == false)
					return false;
				can_draw = this.CheckCoordinates(lower_right_x, lower_right_y);
				if (can_draw == false)
					return false;
			}
	
			
			PDFjet.NET.Point point = null;
			point =  new Point(center.X + this.LeftMargin, center.Y + UpperMargin);
			point.SetRadius(radius);
			point.SetFillShape(true);
			point.SetColor(fill_color);
			point.SetLineWidth(0);
			if (this.SimulateDrawing == false)
				point.DrawOn(this.CurrentPage);
			
			if(line_width > 0)
			{
				point =  new Point(center.X + this.LeftMargin, center.Y + UpperMargin);
				point.SetRadius(radius - line_width / 2);
				point.SetFillShape(false);
				point.SetColor(line_color);
				point.SetLineWidth(line_width);
				if (this.SimulateDrawing == false)
					point.DrawOn(this.CurrentPage);
			}
			return true;
		}
		#endregion circle
		
		
		#region line
		public bool AddAnyLine(PdfPoint start, PdfPoint end, double line_width, int line_color, bool ignore_coordinates)
		{
			PDFjet.NET.Line line = null;

			if(line_width > 0)
			{
				line = new Line(start.X + this.LeftMargin, start.Y + this.UpperMargin, 
				                end.X + this.LeftMargin, end.Y + this.UpperMargin);
				line.SetColor(line_color);
				line.SetWidth(line_width);
				if (this.SimulateDrawing == false)
					line.DrawOn(this.CurrentPage);
			}
			return true;
			
		}
		#endregion line
		
		
		#region rectangle and lines
		public void AddHeaderRectangle(PdfRect rect, double line_width, int line_color)
		{
			this.AddRectangle(rect, line_width, line_color, Layout.NO_COLOR);
		}
		
		public void AddHeaderRectangle(PdfRect rect, int fill_color)
		{
			this.AddRectangle(rect, -1, Layout.NO_COLOR, fill_color);
		}
		
		private void AddHeaderRectangle(PdfRect rect, double line_width, int line_color, int fill_color)
		{
			this.AddRectangle(rect, line_width, line_color, fill_color, true);
		}
		
		public void AddFooterRectangle(PdfRect rect, double line_width, int line_color)
		{
			this.AddRectangle(rect, line_width, line_color, Layout.NO_COLOR);
		}
		
		public void AddFooterRectangle(PdfRect rect, int fill_color)
		{
			this.AddRectangle(rect, -1, Layout.NO_COLOR, fill_color);
		}
		
		private void AddFooterRectangle(PdfRect rect, double line_width, int line_color, int fill_color)
		{
			this.AddRectangle(rect, line_width, line_color, fill_color, true);
		}
		
		public bool AddRectangle(PdfRect rect, double line_width, int line_color)
		{
			return this.AddRectangle(rect, line_width, line_color, Layout.NO_COLOR);
		}
		
		public bool AddRectangle(PdfRect rect, int fill_color)
		{
			return this.AddRectangle(rect, -1, Layout.NO_COLOR, fill_color);
		}
		
		public bool AddRectangle(PdfRect rect, double line_width, int line_color, int fill_color)
		{
			return this.AddRectangle(rect, line_width, line_color, fill_color, false);
		}
		
		private bool AddRectangle(PdfRect rect, double line_width, int line_color, int fill_color, bool ignore_coordinates)
		{
			if (ignore_coordinates == false)
			{
				bool can_draw = this.CheckCoordinates(rect.X, rect.Y);
				if (can_draw == false)
					return false;
				can_draw = this.CheckCoordinates(rect.X + rect.Width, rect.Y + rect.Height);
				if (can_draw == false)
					return false;
			}

			PDFjet.NET.Path path = null;
			
			if (fill_color != Layout.NO_COLOR)
			{
				path = new PDFjet.NET.Path();
				path.Add(new Point(rect.X + this.LeftMargin, rect.Y + this.UpperMargin));
				path.Add(new Point(rect.X + this.LeftMargin + rect.Width, rect.Y + this.UpperMargin));
				path.Add(new Point(rect.X + this.LeftMargin + rect.Width, rect.Y + this.UpperMargin + rect.Height));
				path.Add(new Point(rect.X + this.LeftMargin, rect.Y + this.UpperMargin + rect.Height));	
				path.SetClosePath(true);
				path.SetFillShape(true);
				path.SetColor(fill_color);
				path.SetWidth(0);
				if (this.SimulateDrawing == false)
					path.DrawOn(this.CurrentPage);
			}
			
			if(line_width > 0 && line_color != Layout.NO_COLOR)
			{
				path = new PDFjet.NET.Path();
				double offset = line_width / 2;
				path.Add(new Point(rect.X + this.LeftMargin + offset, rect.Y + this.UpperMargin + offset));
				path.Add(new Point(rect.X + this.LeftMargin + rect.Width - offset, rect.Y + this.UpperMargin + offset));
				path.Add(new Point(rect.X + this.LeftMargin + rect.Width - offset, rect.Y + this.UpperMargin + rect.Height - offset));
				path.Add(new Point(rect.X + this.LeftMargin + offset, rect.Y + this.UpperMargin + rect.Height - offset));
				path.SetClosePath(true);			
				path.SetFillShape(false);
				path.SetColor(line_color);
				path.SetWidth(line_width);
				if (this.SimulateDrawing == false)
					path.DrawOn(this.CurrentPage);
			}
			return true;
		}
		
		
		
		
		
		public void AddHeaderHorizontalLine(double x, double y, double width, double line_width)
		{
			this.AddHorizontalLine(x, y, width, line_width, Color.black, true);
		}
		
		public void AddHeaderHorizontalLine(double x, double y, double width, double line_width, int line_color)
		{
			this.AddHorizontalLine(x, y, width, line_width, line_color, true);
		}
		
		public void AddFooterHorizontalLine(double x, double y, double width, double line_width)
		{
			this.AddHorizontalLine(x, y, width, line_width, Color.black, true);
		}
		
		public void AddFooterHorizontalLine(double x, double y, double width, double line_width, int line_color)
		{
			this.AddHorizontalLine(x, y, width, line_width, line_color, true);
		}
		
		public bool AddHorizontalLine(double x, double y, double width, double line_width)
		{
			return this.AddHorizontalLine(x, y, width, line_width, Color.black, false);
		}
		
		public bool AddHorizontalLine(double x, double y, double width, double line_width, int line_color)
		{
			return this.AddHorizontalLine(x, y, width, line_width, line_color, false);
		}
		
		private bool AddHorizontalLine(double x, double y, double width, double line_width, int line_color, bool ignore_coordinates)
		{
			double offset = line_width / 2;
			
			if (ignore_coordinates == false)
			{
				bool can_draw = this.CheckCoordinates(x, y - offset);
				if (can_draw == false)
					return false;
				can_draw = this.CheckCoordinates(x + width, y + offset);
				if (can_draw == false)
					return false;
			}
		
			if (line_color != Layout.NO_COLOR)
			{
				Line line = new Line(x + this.LeftMargin, y + this.UpperMargin, 
				                     x + this.LeftMargin + width, y + this.UpperMargin);
				line.SetWidth(line_width);
				line.SetColor(line_color);
				if (this.SimulateDrawing == false)
					line.DrawOn(this.CurrentPage);
				
				this.HorizontalLineBoundingBox = new PdfRect(x, y - offset, width, 2 * offset);
			}
			return true;
		}
		
		public PdfRect HorizontalLineBoundingBox {get;set;}
		
		
		
		public void AddHeaderVerticalLine(double x, double y, double width, double line_width)
		{
			this.AddVerticalLine(x, y, width, line_width, Color.black, true);
		}
		
		public void AddHeaderVerticalLine(double x, double y, double width, double line_width, int line_color)
		{
			this.AddVerticalLine(x, y, width, line_width, line_color, true);
		}
		
		public void AddFooterVerticalLine(double x, double y, double width, double line_width)
		{
			this.AddVerticalLine(x, y, width, line_width, Color.black, true);
		}
		
		public void AddFooterVerticalLine(double x, double y, double width, double line_width, int line_color)
		{
			this.AddVerticalLine(x, y, width, line_width, line_color, true);
		}
		
		public bool AddVerticalLine(double x, double y, double width, double line_width)
		{
			return this.AddVerticalLine(x, y, width, line_width, Color.black, false);
		}
		
		public bool AddVerticalLine(double x, double y, double width, double line_width, int line_color)
		{
			return this.AddVerticalLine(x, y, width, line_width, line_color, false);
		}
		
		private bool AddVerticalLine(double x, double y, double width, double line_width, int line_color, bool ignore_coordinates)
		{
			double offset = line_width / 2;
			
			if (ignore_coordinates == false)
			{
				bool can_draw = this.CheckCoordinates(x - offset, y);
				if (can_draw == false)
					return false;
				can_draw = this.CheckCoordinates(x + offset, y + width);
				if (can_draw == false)
					return false;
			}
		
			if (line_color != Layout.NO_COLOR)
			{
				Line line = new Line(x + this.LeftMargin, y + this.UpperMargin, 
				                     x + this.LeftMargin, y + this.UpperMargin + width);
				line.SetWidth(line_width);
				line.SetColor(line_color);
				if (this.SimulateDrawing == false)
					line.DrawOn(this.CurrentPage);
				
				this.VerticalLineBoundingBox = new PdfRect(x - offset, y,  2 * offset, width);
			}
			return true;
		}
		
		public PdfRect VerticalLineBoundingBox {get;set;}
		#endregion rectangle and lines
		
		
		
		
		
		
		#region bitmap
		/*
		public void AddHeaderBitmap(Gdk.Pixbuf buf, PdfPoint point)
		{
			this.AddHeaderBitmap(buf, point, PdfBitmapScales.X, buf.Width);
		}
		
		public void AddHeaderBitmap(Gdk.Pixbuf buf, PdfPoint point, PdfBitmapScales scale_type, double reference_length)
		{
			this.png_file = System.IO.Path.Combine(System.IO.Path.GetTempPath(), 
			                                       System.IO.Path.GetRandomFileName());
			buf.Save(this.png_file, "png");
			PdfRect rect = new PdfRect(point.X, point.Y, buf.Width, buf.Height);
			this.AddBitmap(rect, scale_type, reference_length, true);
		}
		
		public void AddFooterBitmap(Gdk.Pixbuf buf, PdfPoint point)
		{
			this.AddFooterBitmap(buf, point, PdfBitmapScales.X, buf.Width);
		}
		
		public void AddFooterBitmap(Gdk.Pixbuf buf, PdfPoint point, PdfBitmapScales scale_type, double reference_length)
		{
			this.png_file = System.IO.Path.Combine(System.IO.Path.GetTempPath(), 
			                                       System.IO.Path.GetRandomFileName());
			buf.Save(this.png_file, "png");
			PdfRect rect = new PdfRect(point.X, point.Y, buf.Width, buf.Height);
			this.AddBitmap(rect, scale_type, reference_length, true);
		}
		
		public bool AddBitmap(Gdk.Pixbuf buf, PdfPoint point)
		{
			this.png_file = System.IO.Path.Combine(System.IO.Path.GetTempPath(), 
			                                       System.IO.Path.GetRandomFileName());
			buf.Save(this.png_file, "png");
			PdfRect rect = new PdfRect(point.X, point.Y, buf.Width, buf.Height);
			return this.AddBitmap(rect, PdfBitmapScales.X, buf.Width, false);
		}
		
		public bool AddBitmap(Gdk.Pixbuf buf, PdfPoint point, PdfBitmapScales scale_type, double reference_length)
		{
			this.png_file = System.IO.Path.Combine(System.IO.Path.GetTempPath(), 
			                                       System.IO.Path.GetRandomFileName());
			buf.Save(this.png_file, "png");
			PdfRect rect = new PdfRect(point.X, point.Y, buf.Width, buf.Height);
			return this.AddBitmap(rect, scale_type, reference_length, false);
		}
		*/
		
		
		
		
		public void AddHeaderBitmap(System.Drawing.Bitmap bitmap, PdfPoint point)
		{
			this.AddHeaderBitmap(bitmap, point, PdfBitmapScales.X, bitmap.Width);
		}
		
		public void AddHeaderBitmap(System.Drawing.Bitmap bitmap, PdfPoint point, PdfBitmapScales scale_type, double reference_length)
		{
			this.png_file = System.IO.Path.Combine(System.IO.Path.GetTempPath(), 
			                                       System.IO.Path.GetRandomFileName());
			
			bitmap.Save(this.png_file, System.Drawing.Imaging.ImageFormat.Png);	
			PdfRect rect = new PdfRect(point.X, point.Y, bitmap.Width, bitmap.Height);
			this.AddBitmap(rect, scale_type, reference_length, true);
		}
		
		public void AddFooterBitmap(System.Drawing.Bitmap bitmap, PdfPoint point)
		{
			this.AddFooterBitmap(bitmap, point, PdfBitmapScales.X, bitmap.Width);
		}
		
		public void AddFooterBitmap(System.Drawing.Bitmap bitmap, PdfPoint point, PdfBitmapScales scale_type, double reference_length)
		{
			this.png_file = System.IO.Path.Combine(System.IO.Path.GetTempPath(), 
			                                       System.IO.Path.GetRandomFileName());
			
			bitmap.Save(this.png_file, System.Drawing.Imaging.ImageFormat.Png);	
			PdfRect rect = new PdfRect(point.X, point.Y, bitmap.Width, bitmap.Height);
			this.AddBitmap(rect, scale_type, reference_length, true);
		}
		
		public bool AddBitmap(System.Drawing.Bitmap bitmap, PdfPoint point)
		{
			this.png_file = System.IO.Path.Combine(System.IO.Path.GetTempPath(), 
			                                       System.IO.Path.GetRandomFileName());
			
			bitmap.Save(this.png_file, System.Drawing.Imaging.ImageFormat.Png);	
			PdfRect rect = new PdfRect(point.X, point.Y, bitmap.Width, bitmap.Height);
			return this.AddBitmap(rect, PdfBitmapScales.X, bitmap.Width, false);
		}
		
		public bool AddBitmap(System.Drawing.Bitmap bitmap, PdfPoint point, PdfBitmapScales scale_type, double reference_length)
		{
			this.png_file = System.IO.Path.Combine(System.IO.Path.GetTempPath(), 
			                                       System.IO.Path.GetRandomFileName());
			
			bitmap.Save(this.png_file, System.Drawing.Imaging.ImageFormat.Png);	
			PdfRect rect = new PdfRect(point.X, point.Y, bitmap.Width, bitmap.Height);
			return this.AddBitmap(rect, scale_type, reference_length, false);
		}
		
		private bool AddBitmap(PdfRect rect, PdfBitmapScales scale_type, double reference_length, bool ignore_coordiantes)
		{
			FileStream picture_stream = new FileStream( this.png_file, FileMode.Open, FileAccess.Read);
			Image img = new Image(this.PDF, picture_stream, ImageType.PNG);

			try
			{
				File.Delete(this.png_file);
			}
			catch(Exception)
			{
			}
			
			double scale = 0;
			switch(scale_type)
			{
			case PdfBitmapScales.X:
				scale = reference_length / rect.Width;
				break;
				
			case PdfBitmapScales.Y:
				scale = reference_length / rect.Height;
				break;
			}
			
			double width  = rect.Width  * scale;
			double height = rect.Height * scale;
			this.ImageBoundingBox = new PdfRect(rect.X, rect.Y, width, height);
			
			if (ignore_coordiantes == false)
			{
				bool can_draw = this.CheckCoordinates(rect.X, rect.Y);
				if (can_draw == false)
					return false;			
				can_draw = this.CheckCoordinates(rect.X + width, rect.Y + height);
				if (can_draw == false)
					return false;
			}
			
			img.SetPosition(rect.X + this.LeftMargin, rect.Y + this.UpperMargin);
			img.ScaleBy(scale);
			if (this.SimulateDrawing == false)
				img.DrawOn(this.CurrentPage);
			
			return true;
		}
		
		public PdfRect ImageBoundingBox {get;set;}
		
		#endregion bitmap
		
		
		
		#region text
		private List<PdfTerm> Lexer(string text)
        {
			if(String.IsNullOrEmpty(text) == true)
			   text = String.Empty;
			
            text = text.Trim(' ');
			
            List<PdfTerm> terms = new List<PdfTerm>();

			bool opening_bracket 	= false;
			bool white_space 		= false;
			bool word 				= false;
			
			StringBuilder tmp = new StringBuilder();
			
            for (int i = 0; i < text.Length; i++)
			{
				switch (text[i])
                {
                case ' ':
					if (opening_bracket == true || white_space == true)
					{
						tmp.Append(text[i]);
					}
					else
					{
						opening_bracket 	= false;
						white_space			= true;
						word 				= false;
					
						if (tmp.ToString().Length > 0)
							terms.Add(this.GetTerm(tmp.ToString()));
						tmp = new StringBuilder();
						tmp.Append(text[i]);
					}
					break; 
					
				case '\t':
					opening_bracket 	= false;
					white_space			= false;
					word				= false;
					
					if (tmp.ToString().Length > 0)
							terms.Add(this.GetTerm(tmp.ToString()));
					tmp = new StringBuilder();
					tmp.Append(text[i]);
					break;
					
                case '\n':
					opening_bracket 	= false;
					white_space			= false;
					word				= false;
					
					if (tmp.ToString().Length > 0)
							terms.Add(this.GetTerm(tmp.ToString()));
					tmp = new StringBuilder();
					tmp.Append(text[i]);
					break;
					
                case '(':
					opening_bracket 	= true;
					white_space			= false;
					word				= false;
					
					if (tmp.ToString().Length > 0)
							terms.Add(this.GetTerm(tmp.ToString()));
					tmp = new StringBuilder();
					tmp.Append(text[i]);
					break;
					
                case ')':
					if (white_space == true && opening_bracket == false)
					{	
						tmp.Append(text[i]);
						terms.Add(this.GetTerm(tmp.ToString()));
						tmp = new StringBuilder();
					}
					else
					{
						if (tmp.ToString().Length > 0)
							terms.Add(this.GetTerm(tmp.ToString()));
						tmp = new StringBuilder();
						tmp.Append(text[i]);
					}
					opening_bracket 	= false;
					white_space			= false;
					word				= false;
                    break;

               	default: 
					if (word == false)
					{
						opening_bracket 	= false;
						white_space			= false;
						word				= true;
						
						if (tmp.ToString().Length > 0) 
							terms.Add(this.GetTerm(tmp.ToString()));
						tmp = new StringBuilder();
						tmp.Append(text[i]);
					}
					else
					{
						tmp.Append(text[i]);
					}
                    break;
				}
			}
				
			if (tmp.ToString().Length > 0)
							terms.Add(this.GetTerm(tmp.ToString()));
            return terms;
        }
			
		private PdfTerm GetTerm(string text)
		{
			PdfTermtypes type = PdfTermtypes.Word;
			
			if (text.StartsWith("(") == true)
				type = PdfTermtypes.OpeningBracket;
			else if (text.EndsWith(")") == true)
				type = PdfTermtypes.ClosingBracket;
			else if (text.EndsWith("\n") == true)
				type = PdfTermtypes.NewLine;
			else if (text.EndsWith("\t") == true)
				type = PdfTermtypes.Tabulator;
			else if (text.Trim() == String.Empty)
				type = PdfTermtypes.WhiteSpace;
			
			double width = 0;
			string content = String.Empty;
			
			if (type != PdfTermtypes.NewLine)
			{
				if (type == PdfTermtypes.Tabulator)
					content = "    ";
				else
					content = text;
				
				width = this.MeasureString(content);
			}
			
			PdfTerm term = new PdfTerm();
			term.TermType = type;
			term.Content = content;
			term.Width = width;
			
			return term;
		}
		
		//Funtion for Debugging
		private void PrintTerms(List<PdfTerm> terms)
		{
			foreach(PdfTerm term in terms)
			{
				Console.WriteLine(term.TermType.ToString() + " Length: " + term.Width + " Content: <<" + term.Content + ">>");
				Console.Read();
			}
		}
		
		private List<List<PdfTerm>> GenerateLines(List<PdfTerm> terms, double paragraph_width)
		{
			List<List<PdfTerm>> lines = new List<List<PdfTerm>>();
			List<PdfTerm> line = new List<PdfTerm>();
			
			double current_line_width 	= 0;
			
			for(int i = 0; i < terms.Count; i++)
			{
				switch(terms[i].TermType)
				{
				case PdfTermtypes.NewLine:
					lines.Add(line);
					line = new List<PdfTerm>();
					current_line_width = 0;
					break;
					
				case PdfTermtypes.Tabulator:
				case PdfTermtypes.WhiteSpace:
					current_line_width += terms[i].Width;		
					line.Add(terms[i]);
					break;
					
				case PdfTermtypes.Word:
					if (terms[i].Width > paragraph_width)
					{
						string tmp = string.Empty;
						foreach(char c in terms[i].Content)
						{
							double char_width = this.MeasureString(new string(new char[]{c}));
							if ( current_line_width + char_width < paragraph_width)
							{
								tmp += new string(new char[]{c});								
								current_line_width += char_width;								
							}
							else
							{
								if (tmp.Length > 0)
								{
									PdfTerm term = new PdfTerm();
									term.Content = tmp;
									term.Width = this.MeasureString(tmp);
									term.TermType = PdfTermtypes.Word;
									line.Add(term);
								}
								lines.Add(line);
								line = new List<PdfTerm>();
								tmp = new string(new char[]{c});
								current_line_width = char_width;
							}
						}
						if (tmp.Length > 0)
						{
							PdfTerm term = new PdfTerm();
							term.Content = tmp;
							term.Width = this.MeasureString(tmp);
							term.TermType = PdfTermtypes.Word;
							line.Add(term);
						}
					}
					else
					{
						if (terms[i].Width + current_line_width > paragraph_width)
						{
							lines.Add(line);
							line = new List<PdfTerm>();
							line.Add(terms[i]);
							current_line_width = terms[i].Width;
						}
						else
						{
							line.Add(terms[i]);
							current_line_width += terms[i].Width;
						}
					}
					break;		
					
				case PdfTermtypes.OpeningBracket:
					int bracket_counter = 1;
					List<PdfTerm> tmp_terms = new List<PdfTerm>();
					tmp_terms.Add(terms[i]);
					for (int j = i + 1; j < terms.Count; j++) 
					{
						if (terms[j].TermType == PdfTermtypes.OpeningBracket)
							bracket_counter++;
						if(terms[j].TermType == PdfTermtypes.ClosingBracket)
							bracket_counter--;
						if (terms[j].TermType == PdfTermtypes.NewLine)
							break;
						tmp_terms.Add(terms[j]);
						if (bracket_counter == 0)
							break;
					}
					
					if (bracket_counter == 0) //regular
					{
						double bracket_expression_width = 0;
						foreach(PdfTerm term in tmp_terms)
							bracket_expression_width += term.Width;
						if (bracket_expression_width > paragraph_width)
						{
							if (current_line_width + terms[i].Width > paragraph_width)
							{
								lines.Add(line);
								line = new List<PdfTerm>();
								line.Add(terms[i]);
								current_line_width = terms[i].Width;
							}
							else
							{
								line.Add(terms[i]);
								current_line_width += terms[i].Width;
							}
						}
						else
						{
							if (current_line_width + bracket_expression_width > paragraph_width)
							{
								lines.Add(line);
								line = new List<PdfTerm>();
								foreach(PdfTerm term in tmp_terms)
									line.Add(term);
								current_line_width = bracket_expression_width;
							}
							else
							{
								foreach(PdfTerm term in tmp_terms)
									line.Add(term);
								current_line_width += bracket_expression_width;
							}
							i += tmp_terms.Count - 1;
						}
					}
					else
					{
						if (current_line_width + terms[i].Width > paragraph_width)
						{
							lines.Add(line);
							line = new List<PdfTerm>();
							line.Add(terms[i]);
							current_line_width = terms[i].Width;
						}
						else
						{
							line.Add(terms[i]);
							current_line_width += terms[i].Width;
						}
						
					}
					break;
					
				case PdfTermtypes.ClosingBracket:
					if (terms[i].Width > paragraph_width)
					{
						terms[i].Content = terms[i].Content.Trim();
						terms[i].Width = this.MeasureString(terms[i].Content);
					}
					
					if (current_line_width + terms[i].Width > paragraph_width)
					{
						lines.Add(line);
						line = new List<PdfTerm>();
						line.Add(terms[i]);
						current_line_width = terms[i].Width;
					}
					else
					{
						line.Add(terms[i]);
						current_line_width = terms[i].Width;
					}
					break;
				}
			}
			
			if (line.Count > 0)
				lines.Add(line);
			
			return lines;
		}
		
		private List<List<PdfTerm>> TrimLinesRight( List<List<PdfTerm>> lines)
		{
			List<List<PdfTerm>> trimmed_lines = new List<List<PdfTerm>>();
			foreach (var line in lines)
			{
				List<PdfTerm> tmp_line = new List<PdfTerm>();
				bool found_printable = false;
				for (int i = line.Count - 1; i >= 0; i--) 
				{
					switch(line[i].TermType)
					{
					case PdfTermtypes.Tabulator:
					case PdfTermtypes.WhiteSpace:
						if (found_printable == true)
							tmp_line.Insert(0, line[i]);
						break;
						
					case PdfTermtypes.OpeningBracket:
						if (found_printable == false)
							line[i].Content = line[i].Content.TrimEnd();
						found_printable = true;
						tmp_line.Insert(0, line[i]);
						break;
					
					default:
						found_printable = true;
						tmp_line.Insert(0, line[i]);
						break;
					}
				}

                trimmed_lines.Add(tmp_line);
			}
			return trimmed_lines;
		}
		
		private void DrawParagraph(List<List<PdfTerm>> lines, double paragraph_width, PdfTextAlignments alignment)
		{		
			switch(alignment)
			{
			case PdfTextAlignments.Left:
			case PdfTextAlignments.Right:
			case PdfTextAlignments.Center:
				
				double y_pos = this.YPosition;
				foreach(List<PdfTerm> line in lines)
				{
					string tmp_line = String.Empty;
					foreach(PdfTerm term in line)
						tmp_line += term.Content;
						
					double line_width = this.MeasureString(tmp_line);
					double x_pos = 0;
					
					switch(alignment)
					{
					case PdfTextAlignments.Left:
						x_pos = 0;
						break;
					case PdfTextAlignments.Right:
						x_pos = paragraph_width - line_width;
						break;
					case PdfTextAlignments.Center:
						x_pos = (paragraph_width - line_width) / 2;
						break;
					}
					
					TextLine text_line = new TextLine(this.CurrentFont, tmp_line);
					text_line.SetColor(this.FontColor);
					text_line.SetPosition(this.LeftMargin + this.XPosition + x_pos, y_pos + this.UpperMargin + this.FontSize);
					if (this.SimulateDrawing == false)
						text_line.DrawOn(this.CurrentPage);
					y_pos += this.FontSize + this.LineDistance;
				}
				
				break;
				
			case PdfTextAlignments.Block:
				throw new NotImplementedException("AddParagraph - Alignment block is still not implemented");
			}
		}
		
		
		public PdfRect MeasureParagraph(string text)
		{
			double paragraph_width = this.InnerPageWidth - XPosition;
			return this.MeasureParagraph(text, paragraph_width);
		}
		                                
		public PdfRect MeasureParagraph(string text, double paragraph_width)
		{  
			List<PdfTerm> terms 	 	= this.Lexer(text);
			List<List<PdfTerm>> lines 	= this.GenerateLines(terms, paragraph_width);
			lines 						= this.TrimLinesRight(lines);
			
			int number_of_lines = lines.Count == 0 ? 1 : lines.Count;
			double paragraph_height = number_of_lines * this.FontSize;
			paragraph_height += (number_of_lines - 1) * this.LineDistance;
			
			
			
			return new PdfRect(this.XPosition, this.YPosition, 
			                                        paragraph_width, paragraph_height);
		}
		
				
		public void AddHeader(string text)
		{
			this.AddHeader(text, PdfTextAlignments.Left);
		}
		
		public void AddHeader(string text, PdfTextAlignments alignment)
		{
			this.AddParagraph(text, this.InnerPageWidth - this.XPosition, alignment, true);
		}
		
		public void AddHeader(string text, double paragraph_width, PdfTextAlignments alignment)
		{
			this.AddParagraph(text, paragraph_width, alignment, true);
		}
		
		
		public void AddFooter(string text)
		{
			this.AddFooter(text, PdfTextAlignments.Left);
		}
		
		public void AddFooter(string text, PdfTextAlignments alignment)
		{
			this.AddParagraph(text, this.InnerPageWidth - this.XPosition, alignment, true);
		}
		
		public void AddFooter(string text, double paragraph_width, PdfTextAlignments alignment)
		{
			this.AddParagraph(text, paragraph_width, alignment, true);
		}
		
		public bool AddParagraph(string text)
		{
			return this.AddParagraph(text, this.InnerPageWidth - this.XPosition, PdfTextAlignments.Left, false);
		}
		
		public bool AddParagraph(string text, double paragraph_width)
		{
			return this.AddParagraph(text, paragraph_width, PdfTextAlignments.Left, false);
		}
		
		public bool AddParagraph(string text, PdfTextAlignments alignment)
		{
			return this.AddParagraph(text, this.InnerPageWidth - this.XPosition, alignment, false);
		}
		
		public bool AddParagraph(string text, double paragraph_width, PdfTextAlignments alignment)
		{
			return this.AddParagraph(text, paragraph_width, alignment, false);
		}
		
		private bool AddParagraph(string text, double paragraph_width, PdfTextAlignments alignment, bool ignore_coordiantes)
		{
			if (paragraph_width < this.MeasureString("W"))
				return false;
			
			List<PdfTerm> terms 	 	= this.Lexer(text);
			List<List<PdfTerm>> lines 	= this.GenerateLines(terms, paragraph_width);
			lines 						= this.TrimLinesRight(lines);
			
			int number_of_lines = lines.Count == 0 ? 1 : lines.Count;
			double paragraph_height = number_of_lines * this.FontSize;
			paragraph_height += (number_of_lines - 1) * this.LineDistance;
			
			this.ParagraphBoundingBox = new PdfRect(this.XPosition, this.YPosition, 
			                                        paragraph_width, paragraph_height);
			 
			if (ignore_coordiantes == false)
			{
				bool can_draw = this.CheckCoordinates(this.XPosition, this.YPosition);
				if (can_draw == false)
					return false;
				can_draw = this.CheckCoordinates(this.XPosition + paragraph_width, this.YPosition + paragraph_height);
				if (can_draw == false)
					return false;	
			}
						
			this.DrawParagraph(lines, paragraph_width, alignment);
			return true;
		}
		
		public double MeasureString(string text)
		{
			return this.CurrentFont.StringWidth(text);
		}
		
		public PdfRect ParagraphBoundingBox {get;set;}
		
		
		private string CutText(string text, double width)
		{
			if (width < 0 || this.MeasureString(text) <= width)
				return text;
			
			string temp = text;
			for(;;)
			{
				temp = temp.Substring(0, temp.Length - 1);
				text = temp + "...";
				
				if (this.MeasureString(text) <= width)
					break;	
				if(temp.Length == 1)
				{
					text = String.Empty;
					break;
				}
			}
			
			return text;
		}
		
		public bool AddTextLine(string text, double text_width, PdfTextAlignments alignment, int angle)
		{
			text = this.CutText(text, text_width);
			double width = this.MeasureString(text);
		
	
			double radius = 0;	
			switch(alignment)
			{
			case PdfTextAlignments.Left:
				radius = 0;
				break;
			case PdfTextAlignments.Right:
				radius = text_width - width;
				break;
			case PdfTextAlignments.Center:
				radius = (text_width - width) / 2;
				break;
			case PdfTextAlignments.Block:
				radius = 0;
				break;
			}
			
			int inverted_angle = -angle;	
			
			
			double radius_x = Math.Cos(inverted_angle / 180.0 * Math.PI) * radius;
			double radius_y = Math.Sin(inverted_angle / 180.0 * Math.PI) * radius;
			
			
			double correction_x = Math.Sin(inverted_angle / 180.0 * Math.PI) * this.FontSize;
			double correction_y = Math.Cos(inverted_angle / 180.0 * Math.PI) * this.FontSize;
			
			
			double upper_right_x = Math.Cos(angle / 180.0 * Math.PI) * text_width + this.XPosition + this.LeftMargin;
			double upper_right_y = Math.Sin(-angle / 180.0 * Math.PI) * text_width + this.YPosition + this.UpperMargin;
			
			double lower_right_x = upper_right_x - correction_x;
			double lower_right_y = upper_right_y - correction_y;
			
			double lower_left_x = this.XPosition + this.RightMargin - correction_x;
			double lower_left_y = this.YPosition + this.UpperMargin  - correction_y;
			
			if (this.CheckCoordinates(this.XPosition, this.YPosition) == false)
				return false;
			if (this.CheckCoordinates(upper_right_x, upper_right_y) == false)
				return false;
			if (this.CheckCoordinates(lower_right_x, lower_right_y) == false)
				return false;
			if (this.CheckCoordinates(lower_left_x, lower_left_y) == false)
				return false;
			
			
			double x_pos = this.XPosition + this.LeftMargin + radius_x - correction_x;
			double y_pos = this.YPosition + this.UpperMargin + radius_y - correction_y;
			
			TextLine text_line = new TextLine(this.CurrentFont, text);
			text_line.SetColor(this.FontColor);	
			text_line.SetPosition(x_pos, y_pos);
			text_line.SetTextDirection(angle);
			if (this.SimulateDrawing == false)
				text_line.DrawOn(this.CurrentPage);	
			return true;
		}
		#endregion text

	
		
		#region font
		private Font CurrentFont	{get;set;}
		private int FontColor		{get;set;}
		private double FontSize		{get;set;}
		public void SetFont(PDFjet.NET.CoreFont font_type, double font_size, int font_color)
		{
			this.CurrentFont = new Font(this.PDF, font_type);
			this.CurrentFont.SetSize(font_size);
			this.FontColor = font_color;
			this.FontSize = font_size;		
		}
		#endregion font
	}
}