صورت مساله:
- میخواهیم footer پیش فرض PdfReport را که تاریخ را در یک سمت، و شماره صفحه را در سمتی دیگر نمایش میدهد، به عبارت «صفحه x از n» تغییر دهیم.
- میخواهیم در Header گزارش بجای Header پیش فرض PdfReport یکی از قالبهای PDF تهیه شده توسط Open Office را نمایش دهیم (و یا هر ساختار دیگری را).
تمام اجزای PdfReport جهت امکان اعمال تغییرات کلی و توسعه آنها طراحی شدهاند؛ قالبها، هدر، فوتر، منابع داده، قالبهای نمایش سلولها، تعریف توابع تجمعی سفارشی و غیره. جهت سهولت کار، به ازای هر یک از این موارد، پیاده سازیهای پیش فرضی در PdfReport قرار دارند، امکان اگر مورد رضایت شما نیستند ... از بنیان تغییرشان دهید! (و همچنین اگر مورد جالبی را پیاده سازی کردید، میتوانید به عنوان یک وصله جدید ارائه دهید تا به پروژه اضافه شود)
ضمنا این مطالب سفارشی سازی نیاز به آشنایی با ساختار iTextSharp را نیز دارند؛ در حد ایجاد یک جدول ساده باید با iTextSharp آشنا باشید.
مدلهای مورد استفاده:
توسط این مدلها قصد داریم تعدادی فعالیت (Task) را که به تعدادی کاربر انتساب یافته است، نمایش دهیم. همچنین نمایش مقادیر خواص تو در تو نیز در اینجا مد نظر است؛ برای مثال ستونی مانند این:
کدهای کامل مثال را در ادامه ملاحظه خواهید نمود:
به همراه Header سفارشی:
و Footer سفارشی استفاده شده:
البته لازم به ذکر است که تمام این کدها به پوشه Samples سورس پروژه نیز جهت سهولت دسترسی، اضافه شدهاند .
توضیحات:
برای پیاده سازی Header و Footer سفارشی در PdfReport نیاز خواهید داشت تا دو اینترفیس IPageHeader و IPageFooter را پیاده سازی کنید.
ساختار IPageHeader را در ذیل ملاحظه میکنید:
RenderingGroupHeader مرتبط است به مباحث گروه بندی اطلاعات و گزارشات master-detail که در قسمتهای بعد به آنها اشاره خواهد شد. چون در اینجا به آن نیازی نداشتیم، تنها کافی است متد متناظر با آن، null بر گرداند که در کلاس CustomHeader فوق قابل مشاهده است.
متد RenderingReportHeader به ازای تولید هر صفحه جدید، فراخوانی خواهد شد. به عبارتی میتوانید در صفحات مختلف، هدرهای مختلفی را نمایش دهید.
خروجی هر دو متد در اینجا یک جدول از نوع PdfPTable است. بنابراین هر نوع ساختار دلخواهی را که علاقمند هستید به شکل یک PdfPTable ایجاد کرده و بازگشت دهید. این جدول در هدر صفحات ظاهر خواهد شد.
برای نمونه در کلاس CustomHeader، یک قالب تهیه شده توسط Open Office توسط متد توکار PdfImageHelper.GetITextSharpImageFromPdfTemplate دریافت و تبدیل به تصویر میشود. این تصویر از نوع تصاویر قابل درک توسط iTextSharp است و نه اینکه واقعا تبدیل به یک تصویر معمولی مثلا از نوع bmp شود. سپس این تصویر، در یک ردیف از جدولی قرار داده شده و این جدول بازگشت داده میشود.
در کل یا توسط کار با PdfPTable میتوانید یک هدر غیرپیش فرض را طراحی کنید و یا میتوانید توسط ابزارهای بصری مانند Open Office یک قالب خاص را برای آن تهیه کرده و به روشی که ذکر شد و کدهای آنرا ملاحظه میکنید، بارگذاری و استفاده کنید. این قالبها در مسیر Bin\Data سورسهای پروژه قرار داده شدهاند.
ساختار IPageFooter به صورت زیر است:
برای طراحی یک Footer سفارشی کافی است اینترفیس فوق را پیاده سازی کنید که نمونهای از آنرا در کدهای کلاس CustomFooter ملاحظه مینمائید.
متد DocumentOpened، با وهله سازی شیء Document فراخوانی میشود.
متد PageFinished هر بار پیش از اتمام کار صفحه جاری و افزوده شدن آن به Document فراخوانی میگردد.
متد ClosingDocument، در زمان بسته شدن شیء Document فراخوانی خواهد شد.
اگر به امضای این متدها دقت کنید، شیء PdfWriter در اختیار شما قرار گرفته است که توسط آن میتوان مستقیما بر روی فایل PDF، محتوایی را قرار داد. شیء Document نیز در دسترس است. مثلا توسط آن میتوان اندازه دقیق صفحه را بدست آورد.
به علاوه پارامتر columnCellsSummaryData نیز امکان دسترسی به مقادیر ردیفهای قبلی را در اختیار شما قرار میدهد. برای مثال اگر نیاز دارید تا بر اساس مقادیر ستونها و ردیفهای قبلی، محاسباتی را انجام داده و در پایین صفحات درج کنید، به این ترتیب دسترسی کاملی به آنها، خواهید داشت.
استفاده از این کلاسهای سفارشی نیز همواره به شکل زیر خواهد بود:
کلا در PdfReport هر جایی متدی به نام CustomXYZ را مشاهده کردید، این متد یک اینترفیس را دریافت میکند. به عبارتی این امکان را خواهید داشت تا از متدهای پیش فرض مهیا صرفنظر کرده و مطابق نیاز، نسبت به پیاده سازی و استفاده از وهله جدیدی از این اینترفیس تعریف شده، اقدام کنید.
- میخواهیم footer پیش فرض PdfReport را که تاریخ را در یک سمت، و شماره صفحه را در سمتی دیگر نمایش میدهد، به عبارت «صفحه x از n» تغییر دهیم.
- میخواهیم در Header گزارش بجای Header پیش فرض PdfReport یکی از قالبهای PDF تهیه شده توسط Open Office را نمایش دهیم (و یا هر ساختار دیگری را).
تمام اجزای PdfReport جهت امکان اعمال تغییرات کلی و توسعه آنها طراحی شدهاند؛ قالبها، هدر، فوتر، منابع داده، قالبهای نمایش سلولها، تعریف توابع تجمعی سفارشی و غیره. جهت سهولت کار، به ازای هر یک از این موارد، پیاده سازیهای پیش فرضی در PdfReport قرار دارند، امکان اگر مورد رضایت شما نیستند ... از بنیان تغییرشان دهید! (و همچنین اگر مورد جالبی را پیاده سازی کردید، میتوانید به عنوان یک وصله جدید ارائه دهید تا به پروژه اضافه شود)
ضمنا این مطالب سفارشی سازی نیاز به آشنایی با ساختار iTextSharp را نیز دارند؛ در حد ایجاد یک جدول ساده باید با iTextSharp آشنا باشید.
مدلهای مورد استفاده:
namespace PdfReportSamples.Models { public class Task { public int Id { set; get; } public string Name { set; get; } public int PercentCompleted { set; get; } public bool IsActive { set; get; } public User Assignee { set; get; } } }
using System; namespace PdfReportSamples.Models { public class User { public int Id { set; get; } public string Name { set; get; } public string LastName { set; get; } public long Balance { set; get; } public DateTime RegisterDate { set; get; } } }
column.PropertyName<Task>(x => x.Assignee.Name)
using System; using System.Collections.Generic; using System.Drawing; using PdfReportSamples.Models; using PdfRpt.Core.Contracts; using PdfRpt.FluentInterface; namespace PdfReportSamples.CustomHeaderFooter { public class CustomHeaderFooterPdfReport { readonly CustomHeader _customHeader = new CustomHeader(); public IPdfReportData CreatePdfReport() { return new PdfReport().DocumentPreferences(doc => { doc.RunDirection(PdfRunDirection.LeftToRight); doc.Orientation(PageOrientation.Portrait); doc.PageSize(PdfPageSize.A4); doc.DocumentMetadata(new DocumentMetadata { Author = "Vahid", Application = "PdfRpt", Keywords = "Test", Subject = "Test Rpt", Title = "Test" }); }) .DefaultFonts(fonts => { fonts.Path(Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\tahoma.ttf", Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\verdana.ttf"); }) .PagesFooter(footer => { footer.CustomFooter(new CustomFooter(footer.PdfFont, PdfRunDirection.LeftToRight)); }) .PagesHeader(header => { header.CustomHeader(_customHeader); }) .MainTableTemplate(template => { template.BasicTemplate(BasicTemplate.SilverTemplate); }) .MainTablePreferences(table => { table.ColumnsWidthsType(TableColumnWidthType.Relative); table.MultipleColumnsPerPage(new MultipleColumnsPerPage { ColumnsGap = 22, ColumnsPerPage = 2, ColumnsWidth = 250, IsRightToLeft = false, TopMargin = 7 }); }) .MainTableDataSource(dataSource => { var rows = new List<Task>(); var rnd = new Random(); for (int i = 1; i < 210; i++) { rows.Add(new Task { Assignee = new User { Id = i, Name = "user-" + i }, IsActive = rnd.Next(0, 2) == 1 ? true : false, Name = "task-" + i }); } dataSource.StronglyTypedList(rows); }) .MainTableColumns(columns => { columns.AddColumn(column => { column.PropertyName("rowNo"); column.IsRowNumber(true); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.IsVisible(true); column.Order(0); column.Width(1); column.HeaderCell("#"); }); columns.AddColumn(column => { column.PropertyName<Task>(x => x.Name); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.IsVisible(true); column.Order(1); column.Width(3); column.HeaderCell("Task Name"); }); columns.AddColumn(column => { column.PropertyName<Task>(x => x.Assignee.Name); // nested property support column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.IsVisible(true); column.Order(2); column.Width(3); column.HeaderCell("Assignee"); }); columns.AddColumn(column => { column.PropertyName<Task>(x => x.IsActive); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.IsVisible(true); column.Order(3); column.Width(2); column.HeaderCell("Active"); column.ColumnItemsTemplate(template => { template.Checkmark(checkmarkFillColor: Color.Green, crossSignFillColor: Color.DarkRed); }); }); }) .MainTableEvents(events => { events.DataSourceIsEmpty(message: "There is no data available to display."); }) .Export(export => { export.ToExcel(); }) .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\CustomHeaderFooterPdfReportSample.pdf")); } } }
به همراه Header سفارشی:
using System.Collections.Generic; using iTextSharp.text; using iTextSharp.text.pdf; using PdfRpt.Core.Contracts; using PdfRpt.Core.Helper; namespace PdfReportSamples.CustomHeaderFooter { public class CustomHeader : IPageHeader { public PdfPTable RenderingGroupHeader(Document pdfDoc, PdfWriter pdfWriter, IList<CellData> rowdata, IList<SummaryCellData> summaryData) { return null; } Image _image; public PdfPTable RenderingReportHeader(Document pdfDoc, PdfWriter pdfWriter, IList<SummaryCellData> summaryData) { if (_image == null) //cache is empty { var templatePath = AppPath.ApplicationPath + "\\data\\PdfHeaderTemplate.pdf"; _image = PdfImageHelper.GetITextSharpImageFromPdfTemplate(pdfWriter, templatePath); } var table = new PdfPTable(1); var cell = new PdfPCell(_image, true) { Border = 0 }; table.AddCell(cell); return table; } } }
و Footer سفارشی استفاده شده:
using System.Collections.Generic; using iTextSharp.text; using iTextSharp.text.pdf; using PdfRpt.Core.Contracts; namespace PdfReportSamples.CustomHeaderFooter { public class CustomFooter : IPageFooter { PdfContentByte _pdfContentByte; readonly IPdfFont _pdfRptFont; readonly Font _font; readonly PdfRunDirection _direction; PdfTemplate _template; public CustomFooter(IPdfFont pdfRptFont, PdfRunDirection direction) { _direction = direction; _pdfRptFont = pdfRptFont; _font = _pdfRptFont.Fonts[0]; } public void ClosingDocument(PdfWriter writer, Document document, IList<SummaryCellData> columnCellsSummaryData) { _template.BeginText(); _template.SetFontAndSize(_pdfRptFont.Fonts[0].BaseFont, 8); _template.SetTextMatrix(0, 0); _template.ShowText((writer.PageNumber - 1).ToString()); _template.EndText(); } public void PageFinished(PdfWriter writer, Document document, IList<SummaryCellData> columnCellsSummaryData) { var pageSize = document.PageSize; var text = "Page " + writer.PageNumber + " / "; var textLen = _font.BaseFont.GetWidthPoint(text, _font.Size); var center = (pageSize.Left + pageSize.Right) / 2; var align = _direction == PdfRunDirection.RightToLeft ? Element.ALIGN_RIGHT : Element.ALIGN_LEFT; ColumnText.ShowTextAligned( canvas: _pdfContentByte, alignment: align, phrase: new Phrase(text, _font), x: center, y: pageSize.GetBottom(25), rotation: 0, runDirection: (int)_direction, arabicOptions: 0); var x = _direction == PdfRunDirection.RightToLeft ? center - textLen : center + textLen; _pdfContentByte.AddTemplate(_template, x, pageSize.GetBottom(25)); } public void DocumentOpened(PdfWriter writer, IList<SummaryCellData> columnCellsSummaryData) { _pdfContentByte = writer.DirectContent; _template = _pdfContentByte.CreateTemplate(50, 50); } } }
البته لازم به ذکر است که تمام این کدها به پوشه Samples سورس پروژه نیز جهت سهولت دسترسی، اضافه شدهاند .
توضیحات:
برای پیاده سازی Header و Footer سفارشی در PdfReport نیاز خواهید داشت تا دو اینترفیس IPageHeader و IPageFooter را پیاده سازی کنید.
ساختار IPageHeader را در ذیل ملاحظه میکنید:
using System.Collections.Generic; using iTextSharp.text; using iTextSharp.text.pdf; namespace PdfRpt.Core.Contracts { public interface IPageHeader { PdfPTable RenderingGroupHeader(Document pdfDoc, PdfWriter pdfWriter, IList<CellData> newGroupInfo, IList<SummaryCellData> summaryData); PdfPTable RenderingReportHeader(Document pdfDoc, PdfWriter pdfWriter, IList<SummaryCellData> summaryData); } }
RenderingGroupHeader مرتبط است به مباحث گروه بندی اطلاعات و گزارشات master-detail که در قسمتهای بعد به آنها اشاره خواهد شد. چون در اینجا به آن نیازی نداشتیم، تنها کافی است متد متناظر با آن، null بر گرداند که در کلاس CustomHeader فوق قابل مشاهده است.
متد RenderingReportHeader به ازای تولید هر صفحه جدید، فراخوانی خواهد شد. به عبارتی میتوانید در صفحات مختلف، هدرهای مختلفی را نمایش دهید.
خروجی هر دو متد در اینجا یک جدول از نوع PdfPTable است. بنابراین هر نوع ساختار دلخواهی را که علاقمند هستید به شکل یک PdfPTable ایجاد کرده و بازگشت دهید. این جدول در هدر صفحات ظاهر خواهد شد.
برای نمونه در کلاس CustomHeader، یک قالب تهیه شده توسط Open Office توسط متد توکار PdfImageHelper.GetITextSharpImageFromPdfTemplate دریافت و تبدیل به تصویر میشود. این تصویر از نوع تصاویر قابل درک توسط iTextSharp است و نه اینکه واقعا تبدیل به یک تصویر معمولی مثلا از نوع bmp شود. سپس این تصویر، در یک ردیف از جدولی قرار داده شده و این جدول بازگشت داده میشود.
در کل یا توسط کار با PdfPTable میتوانید یک هدر غیرپیش فرض را طراحی کنید و یا میتوانید توسط ابزارهای بصری مانند Open Office یک قالب خاص را برای آن تهیه کرده و به روشی که ذکر شد و کدهای آنرا ملاحظه میکنید، بارگذاری و استفاده کنید. این قالبها در مسیر Bin\Data سورسهای پروژه قرار داده شدهاند.
ساختار IPageFooter به صورت زیر است:
using iTextSharp.text; using iTextSharp.text.pdf; using System.Collections.Generic; namespace PdfRpt.Core.Contracts { public interface IPageFooter { void DocumentOpened(PdfWriter writer, IList<SummaryCellData> columnCellsSummaryData); void PageFinished(PdfWriter writer, Document document, IList<SummaryCellData> columnCellsSummaryData); void ClosingDocument(PdfWriter writer, Document document, IList<SummaryCellData> columnCellsSummaryData); } }
برای طراحی یک Footer سفارشی کافی است اینترفیس فوق را پیاده سازی کنید که نمونهای از آنرا در کدهای کلاس CustomFooter ملاحظه مینمائید.
متد DocumentOpened، با وهله سازی شیء Document فراخوانی میشود.
متد PageFinished هر بار پیش از اتمام کار صفحه جاری و افزوده شدن آن به Document فراخوانی میگردد.
متد ClosingDocument، در زمان بسته شدن شیء Document فراخوانی خواهد شد.
اگر به امضای این متدها دقت کنید، شیء PdfWriter در اختیار شما قرار گرفته است که توسط آن میتوان مستقیما بر روی فایل PDF، محتوایی را قرار داد. شیء Document نیز در دسترس است. مثلا توسط آن میتوان اندازه دقیق صفحه را بدست آورد.
به علاوه پارامتر columnCellsSummaryData نیز امکان دسترسی به مقادیر ردیفهای قبلی را در اختیار شما قرار میدهد. برای مثال اگر نیاز دارید تا بر اساس مقادیر ستونها و ردیفهای قبلی، محاسباتی را انجام داده و در پایین صفحات درج کنید، به این ترتیب دسترسی کاملی به آنها، خواهید داشت.
استفاده از این کلاسهای سفارشی نیز همواره به شکل زیر خواهد بود:
readonly CustomHeader _customHeader = new CustomHeader(); //... .PagesFooter(footer => { footer.CustomFooter(new CustomFooter(footer.PdfFont, PdfRunDirection.LeftToRight)); }) .PagesHeader(header => { header.CustomHeader(_customHeader); })