// 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.IO;
using System.Text;
using Hiperscan.Unix;
using PDFjet.NET;


namespace Hiperscan.PdfLib
{
    public static partial class Pdf
    {
        public static void Open(string uri, string destination)
        {
            Console.WriteLine("Open document {0} -> {1}", uri, destination);

            string file = System.IO.Path.GetFullPath(uri);
            if (string.IsNullOrEmpty(destination))
            {
                Process.OpenUri(file);
                return;
            }

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

            if (Environment.OSVersion.Platform == PlatformID.Unix)
            {
                if (FileIO.ExistsFile("/usr/bin/evince"))
                    System.Diagnostics.Process.Start("evince", file + " -n " + destination);
                else if (FileIO.ExistsFile("/usr/bin/acroread"))
                    System.Diagnostics.Process.Start("acroread", "/a nameddest=" + destination + " " + file);
                else
                    Process.OpenUri(file);
            }
            else
            {
                Pdf.StartAdobeReader(file, destination);
            }
        }

        public static void Open(string fname)
        {
            Pdf.Open(fname, string.Empty);
        }

        private static void StartAdobeReader(string file, string destination)
        {
            Microsoft.Win32.RegistryKey adobe = null;
            try
            {
                adobe = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software").OpenSubKey("Microsoft").OpenSubKey("Windows").OpenSubKey("CurrentVersion").OpenSubKey("App Paths").OpenSubKey("AcroRd32.exe");
                Console.WriteLine("Try to start Adobe 32 bit with key {0}...",
                                  adobe == null ? "Not Found" : adobe.GetValue("").ToString());

                System.Diagnostics.Process.Start(adobe.GetValue("").ToString(), "/a nameddest=" + destination + " " + file);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Start Adobe 32 bit failed: {0}", ex.Message);
                try
                {
                    adobe = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software").OpenSubKey("Microsoft").OpenSubKey("Windows").OpenSubKey("CurrentVersion").OpenSubKey("App Paths").OpenSubKey("Acrobat.exe");
                    Console.WriteLine("Try to start Adobe 64 bit with key {0}...",
                                  adobe == null ? "Not Found" : adobe.GetValue("").ToString());

                    System.Diagnostics.Process.Start(adobe.GetValue("").ToString(), "/a nameddest=" + destination + " " + file);

                }
                catch (Exception ex2)
                {
                    Console.WriteLine("Start Adobe 64 bit failed: {0}", ex2.Message);
                    Console.WriteLine("Open pdf without Acrobat Reader...");
                    System.Diagnostics.Process.Start(file);
                }
            }
        }
    }
    
    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 readonly static int Transparence = -1;
    }
    
    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;
            this.BackgroundColor = PdfColors.Transparence;
        }
        
        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 int BackgroundColor { 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;
                    int background_color = this.TableCells[i, j].BackgroundColor;
                    if (this.Document.AddRectangle(rect, this.BorderWidth, border_color, background_color) == false)
                        throw new PdfLayoutException(Catalog.GetString("Table exceeds page size."));

                    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 partial class Layout
    {
        public const int NO_COLOR = -1;

        public PDFjet.NET.PDF PDF { get; private set; }
        public BufferedStream PdfBuffer { get; private set; }
        private FileStream FileStream { get; 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;
            
            this.FileStream = new FileIO.FileStreamWithMeasurement(file_name, FileMode.Create);
            this.PdfBuffer = new BufferedStream(this.FileStream);
            
            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.Flush();
            this.FileStream.Flush();
            this.PdfBuffer.Close();
            this.FileStream.Dispose();
        }
        
        
        #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 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;

            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
    }
}
