Makale Özeti

Geliştirdiğimiz uygulamalarda hazırladığımız raporları bazı durumlarda belirli dosyalarda saklama ihtiyacı duyabiliyoruz. Yazımızda bu tarz raporları ücretsiz olarak temin edebileceğimiz iTextSharp kütüphanesini kullanarak PDF dosyalarında nasıl oluşturabileceğimizi inceleyeceğiz.

Makale

Kimi zaman geliştirdiğimiz uygulamalarda raporlara ihtiyaç duyuyoruz. Bu raporları oluşturmak için çeşitli araçlar kullanıyoruz. Bu araçlardan birisi de PDF(Portable Document Format - Taşınabilir Belge Biçimi) dosyalarıdır. Bu yazımızda, basit anlamda hazırlayacağımız raporları PDF olarak görüntülemek amacıyla kullanabileceğimiz iTextsharp adında bir kütüphaneden bahsetmek istiyorum.

iTextsharp dll dosyasını http://sourceforge.net/projects/itextsharp adresinden ücretsiz olarak indirebilirsiniz. Ayrıca yapacağımız uygulamada veritabanı olarak AdventureWorks veritabanını kullandım. http://msftdbprodsamples.codeplex.com/ adresinden Microsoft Sql Server için hazırlanmış örnek veritabanlarını indirip bilgisayarınıza kurabilirsiniz.

Uygulama için gerekli olan veritabanı ve dll’i hazırladıktan sonra artık işin kod kısmına geçebiliriz.

NOT: Örneği VS 2010 Express sürümünü üzerinde bir WPF uygulaması kullanarak hazırlayacağım.

Uygulamamıza öncelikle Solution Explorer->References->Add Reference diyerek iTextsharp dll’ini ekliyoruz. Daha sonra veritabanına işlemleri için Add->NewItem->Ado.Net Entity Data Model diyerek .edm dosyasını ekliyoruz. .edm dosyasının adını verdikten sonra çıkan Entity Data Model Wizard ekranında Generate From Database’i seçip ilerliyoruz. Bir sonraki ekranda New Connection deyip açılan ekranda Data Source olarak Microsoft SQL Server Database File; Data Provider olarak da .Net Framework Data Provider for SQL Server’ı seçiyoruz. Continue dediğimizde çıkan ekranda browse diyerek AdventureWorks veritabanını seçiyoruz. Dilerseniz Test Connection diyerek bağlantınızın çalışıp çalışmadığını kontrol edebilirsiniz. Bu işlem tamamlandıktan sonra tekrar bağlantı oluşturduğumuz ekrana dönüyoruz ve ilerliyoruz.

Eğer AdventureWorks veritabanı projeyi oluşturduğunuz klasör içinde değilse karşınıza aşağıdaki gibi bir ekran çıkacak ve veritabanının proje klasörüne kopyalayıp bağlantı cümlesini yeniden yapılandırmak isteyip istemediğinizi soracak. Burada seçim size kalmış dilerseniz veritabanını proje klasörüne kopyalayabilirsiniz yalnız bu durumda AdventureWorks MDF dosyasının 175 mb. civarı bir boyutu olduğunu unutmayın ry tablolarını seçip EDM’ye dahil ediyoruz ve işlemi tamamlıyoruz.

Şimdi sıra geldi raporumuzu oluşturacak metodumuzu yazmaya. Kod kalabalığını ortadan kaldırmak amacıyla metodu 3 ayrı alt metoda bölmeyi tercih ettim :

  • Tablomuzun başlık kısmını oluşturacak olan GetHeaderTable metodu
  • Tablomuzun kolon başlıklarını oluşturacak olan GetColumnTable metodu
  • Tablomuzun değerlerini getirecek olan GetValueTable metodu

Açtığımız Window’un .cs uzantılı kod kısmına geçiyoruz. Öncelikle gerekli olan global değişkenleri tanımlıyoruz.

BaseFont _bFont;

Phrase _emptyString;

int _normalFontSize = 7;

AdventureWorks_DataEntities _dt = new AdventureWorks_DataEntities();

Daha sonra Constructor’da gerekli nesneleri örnekliyoruz.

_bFont = BaseFont.CreateFont(@"C:\WINDOWS\Fonts\tahoma.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); //Türkçe karakter sorununu çözmek amacıyla dökümanımızda kullanmak üzere bilgisayarımızda bulunan fontlardan birini seçiyoruz ve base font olarak olarak tanımlıyoruz. Daha sonra oluşturacağımız tüm fontları bu base fonttan kalıtıyoruz.

_emptyString = new Phrase("x", new Font(_bFont, _normalFontSize, Font.NORMAL, BaseColor.WHITE));// İçinde veri olmayan bir hücre oluşturmamız gerektiğinde hücrenin değerine bu şekilde bir metin vermemiz gerekiyor. Aksi taktirde boş hücre yok sayılıp bir sonraki hücre onun yerine ekleniyor. Bu da tablomuzu oluştururken hata oluşmasına dolayısıyla tablonun oluşturulamamasına ya da yanlış bir tablo oluşmasına neden oluyor.

Burada BaseFont tipinden bir nesne tanımlayıp örneklememizin ve bundan sonra oluşturacak olduğumuz Font tipinden nesneleri bu base font’tan kalıtacak olmamızın sebebi Türkçe karakter sorununun önüne geçmek.   

    public void GetHeaderTable(Document document)
    {

         #region Fields

         BaseColor _headerColor = new BaseColor(000, 019, 127);

         Font _headerFont = new Font(_bFont, 14, Font.BOLD, _headerColor);

         #endregion

         #region Başlık

         PdfPTable header = new PdfPTable(new float[] { 500 });// hücrelerin boyutlarını tabloyu oluştururken verebileceğimiz gibi daha sonra SetWidths metodunu kullanarak da verebiliriz.

         PdfPCell cell = new PdfPCell(new Phrase(string.Format("ÜRÜN LİSTESİ"), _headerFont));// Phrase nesnesini oluşturarak hücreye yazılacak metni oluşturuyoruz.

         cell.HorizontalAlignment = Element.ALIGN_CENTER;

         cell.Border = iTextSharp.text.Rectangle.NO_BORDER;

         header.AddCell(cell);

         #region Boşluk

         cell = new PdfPCell(_emptyString);

         cell.Border = iTextSharp.text.Rectangle.NO_BORDER;

         header.AddCell(cell);

         #endregion

         #endregion

         document.Add(header);

    }

Tablo içindeki hücrelerin genişliklerini tabloyu örneklerken verebileceğimiz gibi daha sora SetWidths metodunu kullanarak da verebiliriz.

 PdfPTable header = new PdfPTable(new float[] { 500 });

Ya da  

PdfPTable header = new PdfPTable(1); //buradaki 1 değeri tablonun kaç hücreli olduğunu gösterir.

header.SetWidths(new float[] { 500 });

    public void GetColumnTable(Document document)
    {

         #region Fields

         BaseColor _columnHeaderColor = new BaseColor(000, 019, 127);

         Font _columnHeaderFont = new Font(_bFont, _normalFontSize, Font.BOLD, _columnHeaderColor);

         #endregion

         #region MainTable

         #region MainTable

         PdfPTable mainTable = new PdfPTable(new float[] { 50, 150, 300 });

         mainTable.DefaultCell.BorderWidth = 1;

         mainTable.DefaultCell.BorderColor = BaseColor.BLACK;

         #endregion

         #region CellProperties

         PdfPCell cell = new PdfPCell();

         cell.HorizontalAlignment = Element.ALIGN_CENTER;

         cell.VerticalAlignment = Element.ALIGN_MIDDLE;

         #endregion

         #region Order

         cell.Phrase = new Phrase("SIRA NO", _columnHeaderFont);

         mainTable.AddCell(cell);

         #endregion

         #region SubCategory

         cell.Phrase = new Phrase("ALT KATEGORİ", _columnHeaderFont);

         mainTable.AddCell(cell);

         #endregion

         #region productTable

         #region productTable Details

         PdfPTable productTable = new PdfPTable(new float[] { 200, 100 });

         #endregion

         #region Product

         cell.Phrase = new Phrase("ÜRÜN", _columnHeaderFont);

         cell.Colspan = 2;//iki hücreyi birleştirmek amacıyla Colspan property'sini kullanabilirsiniz.

         productTable.AddCell(cell);

         #endregion

         cell.Colspan = 1;// her seferinde yeni bir cell nesnesi örneklemediğimiz için Colspan property'sini 1 yapıyoruz.

         #region ProductName

         cell.Phrase = new Phrase("ADI", _columnHeaderFont);

         cell.Rotation=90; //metnin hücre içinde dikey görünmesini istersek öncelikle Rotation property'sini 270 olarak set ediyoruz.

         cell.Rotate(); // Rotate metodunu çağırarak hücreyi döndürüyoruz.

         productTable.AddCell(cell);

         #endregion

         #region ListPrice

         cell.Phrase = new Phrase("LİSTE FİYATI", _columnHeaderFont);

         productTable.AddCell(cell);

         #endregion

         #endregion

         mainTable.AddCell(productTable);

         #endregion

         document.Add(mainTable);
}

Tablo oluştururken bazen resimdeki gibi, bir hücreyi iki veya daha fazla satır ya da kolona bölmemiz gerekebilir.

Bu durumda şöyle bir yol izliyoruz: Öncelikle yeni bir tablo oluşturup (#region productTable ile başlayan alt tablomuz) bu tablonun değerlerini set ediyoruz.Daha sonrasında yine ana tablomuzda hücreleri oluşturduğumuz yolla bu tablo için de gerekli hücreleri oluşturup tabloya ekliyoruz.

Bir hücreyi birleştirmek amacıyla HTML tablolarında kullandığımız Colspan attribute’u gibi burada da Colspan property’si mevcut.   

cell.Colspan = 2;

 şeklinde bir atama ile bu işlemi gerçekleştirebilirsiniz.

Ayrıca tablo içindeki bir hücrenin metninin dikey olmasını da sağlayabilirsiniz, bunun için yapmanız gereken cell.Rotation = 90; şeklinde bir atama işlemi ile Rotation property’sinin değerini set etmek ve ardından cell.Rotate(); metodunu çağırmak.

NOT: Rotation property’sine sadece 90 ve katları değerler verebilirsiniz aksi taktirde uygulama exception fırlatır.

Alt tabloyla ilgili işlemleri tamamladıktan sonra ana tablomuza bu tabloyu ekliyoruz.

mainTable.AddCell(productTable);

Son olarak da tablomuza değerlerin eklendiği metodun içeriğini inceliyoruz.

       public void GetValueTable(Document document)

        {

            #region Fields

            int order = 1;

            Font _nFont = new Font(_bFont, _normalFontSize);

            #endregion

            #region MainTable

            #region MainTable Details

            PdfPTable mainTable = new PdfPTable(new float[] { 50, 150, 300 });

            mainTable.DefaultCell.BorderWidth = 1;

            mainTable.DefaultCell.BorderColor = BaseColor.BLACK;

            #endregion

            #region CellProperties

            PdfPCell cell = new PdfPCell();

            cell.HorizontalAlignment = Element.ALIGN_CENTER;

            cell.VerticalAlignment = Element.ALIGN_MIDDLE;

            #endregion

 

            foreach (var subCategory in _dt.ProductSubcategory)

            {

                #region Order

                cell.Phrase = new Phrase(order.ToString(), _nFont);

                mainTable.AddCell(cell);

                #endregion

                #region SubCategoryName

                cell.Phrase = new Phrase(subCategory.Name, _nFont);

                mainTable.AddCell(cell);

                #endregion

                #region productTable

                #region productTable Details

                PdfPTable productTable = new PdfPTable(new float[] { 200, 100 });

                #endregion

                foreach (var product in subCategory.Product)

                {

                    #region ProductName

                    cell.Phrase = new Phrase(product.Name, _nFont);

                    productTable.AddCell(cell);

                    #endregion

                    #region ListPrice

                    cell.Phrase = new Phrase(product.ListPrice.ToString("F2"), _nFont);

                    productTable.AddCell(cell);

                    #endregion

                }

                #endregion

                mainTable.AddCell(productTable);

                order++;

            }

            #endregion

            document.Add(mainTable);

        }

Burada yukarıda örneklediğimiz _dt (AdventureWorks_DataEntities) nesnesinin ProdcuctSubcategory tablosu içinde foreach döngüsüyle gezerek her bir alt kategoriye bağlı bulunan ürünlerin adını ve liste fiyatını tablomuza ekliyoruz. Ürünlerin adını ve liste fiyatını tabloya eklerken yine productTable adında bir alt tablo oluşturarak mainTable tablomuzun ilgili hücresine her döngüde bu alt tabloyu ekliyoruz.

Son olarak da tüm bu metotları toplayıp PDF dökümanımızı oluşturan metodumuz;

       public string GetTable()

        {

            Document document = new Document(PageSize.A4);//Sayfa boyutunu belirtiyoruz. Dilersek Rotate() metodunu kullanarak sayfayı yatay olarak kullanabiliriz.

            #region Fields

            string _fileName = string.Format(@"{0}Ürün Listesi.pdf", AppDomain.CurrentDomain.BaseDirectory);

            #endregion

            try

            {

                PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(_fileName, FileMode.Create));

                document.Open();

                GetHeaderTable(document);

                GetColumnTable(document);

                GetValueTable(document);

                return _fileName;

            }

            catch (DocumentException docEx)

            {

                MessageBox.Show(string.Format("Hata:{0}", docEx.Message), "Hata", MessageBoxButton.OK, MessageBoxImage.Error);

                return string.Empty;

            }

            finally

            {

                document.Close();

            }

 

        }

Burada öncelikle Document nesnesi aracılığıyla PDF dökümanımızı oluşturuyoruz. Document nesnemizi örneklerken PageSize enum’ını kullanarak hangi tip sayfada göstereceğinizi belirtebilirsiniz.    

Document document = new Document(PageSize.A4);

Document nesnesinin PageSize property’si readonly only olduğu için sayfa tipini daha sonradan verme şansımız yok. Ayrıca burada aşağıdaki gibi bir kullanımla tabloyu sayfaya yatay olarak da yerleştirebilirsiniz.    

Document document = new Document(PageSize.A4.Rotate());

PDF dosyasını istediğiniz bir yere kaydedip buradan gösterebilirsiniz. Ben uygulamanın olduğu Debug klasörünün içine kaydettim.

NOT: Klasik Windows uygulamalarından hatırlayacaksınız bir uygulamanın bulunduğu yolu almak için Application.StartupPath property’sini kullanıyorduk; ancak WPF uygulamalarında durum biraz değişmiş. WPF’ de yeni olduğum için ben de bu uygulama sayesinde bunu öğrenmiş oldumUygulamanın bulunduğu yolu AppDomain.CurrentDomain.BaseDirectory property’sinden elde ediyoruz.

Uygulamayı kaydetmek amacıyla aşağıdaki gibi bir PdfPWriter nesnesi örnekliyoruz.    

PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(_fileName, FileMode.Create));

Document nesnemizi açıp tabloları oluşturmak için yazdığımız 3 metodu çağırıyoruz ve document nesnesiyle işimiz bittiğinde kapatıyoruz.

Bu kadar koddan sonra şimdi sıra geldi oluşturduğumuz PDF dökümanını görüntülemeye. Bunun için Designer kısmına geçip ekrana bir Button bir de WebBrowser nesnesi ekliyoruz. Daha sonra butonun Click eventinde aşağıdaki kodu yazıyoruz.    

string path = GetTable();

wbReport.Navigate(new Uri(path));

NOT: WebBrowser kontrolünün Navigate metoduna parametre olarak path nesnesini verdiğinizde aşağıdaki gibi bir hata ile karşılaşıyorsunuz. Bu nedenle bir Uri nesnesi örnekleyip path değişkenini bu uri’ye parametre olarak verip oluşturduğunuz bu Uri nesnesini de WebBrowser kontrolünün Navigate metoduna parametre olarak vermeniz gerekiyor.

Tüm bu işlemlerin sonunda aşağıdaki gibi bir ekran görüntüsü elde ediyoruz.

Görüldüğü gibi Foxit Reader isimli programdan oluşturduğumuz PDF dokümanını sorunsuz şekilde görebiliyoruz.

Böylece yazımızın sonuna geldik. Bu yazımda iTextSharp.dll dosyasını kullanarak raporlarımızın bir PDF dosyası olarak nasıl oluşturulacağını inceledik. Anlatımdan kaynaklanan eksikliklerden dolayı şimdiden affınıza sığınıyorum. Bir sonraki yazıda görüşmek dileğiyle.

Canan Gümrükçüoğlu
http://canangumrukcuoglu.wordpress.com