Pokazywanie postów oznaczonych etykietą pdf. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą pdf. Pokaż wszystkie posty

niedziela, 31 lipca 2016

Review of tools for PDF generate in .net

 This is my first blog post in English, so fill free to add your comment if any part is incomprehensible.

We have many pdf libraries that we can use, but none of them are perfect and all of them have some advantages and disadvantages. Here is my little experience with generating PDF file in .net framework with generating reports both, in a browser on demand and large numbers in bash/console by night.

Crystal_Reports - big, commercial library develop by big company
Pros: 
- can be really fast
Cons: 
- ugly (at least not pretty) reports
- works on my machine issue (your report can be seen different in different machine, in example on server what can be frustrating when troubleshooting). 

SQL_Server_Reporting_Services - big library that comes with commercial licence for MS SQL Server
Pros:
- quite pretty
- have many in common with office / excel, so you BI can help you in building reports
- can use custom fonts
Cons:
Local Mode:
- have memory leaks (you will see it when you try generate PDF in large numbers)
- more complicated reports can be really slow (reporting services team upgrading reporting services from .net 3.5 to 4.0 made bug with grouping rows in table).
- don't have justify text 
- you can't "copy/paste" document from MS Word
Server Mode:
- sometimes there are issue with authentication and priviliges
- you can't use collection of your objects as data source (it was a reason why I choose Local Mode).
- don't have justify text 
- you can't "copy/paste" document from MS Word

Wkhtmltopdf - open source library made by google for converting html into pdf
Pros:
- quite fast
Cons:
- graph from jQuery library are medium quality (in magnification you can see pixels)
- few options for editing text

MS Word Interop - native MS Word API for CRUD operations in word documents
Pros:
- best MS Word document projection
Cons:
- slow
- need to have MS Office Word installed on working machine
- requires a little work and workarounds when we want to use it from task scheduler ( http://stackoverflow.com/questions/1006923/automating-office-via-windows-service-on-server-2008/1680214#1680214 )
- requires a much more work when we want to use it from IIS 

Telerik - Kendo-UI - commercial library to creating great html, and export it to pdf
Pros:
- quite pretty
- write html and just export it into pdf
Cons:
- have trouble with custom fonts 
- pdf generation could be faster 

czwartek, 18 września 2014

Generowanie PDF z wykorzystaniem Nancy, Pechkin, wkhtmltopdf

Jednym z zadań, jakie dostałem było wygenerowanie raportu, który równocześnie miał się wyświetlać jako html, jak i pdf. Moją pierwszą myślą, było stworzenie tego raportu jako www, a nast. zamiana tego www w pdf. Dodatkowo, chciałem dać użytkownikowi, aby sam wybrał, czy rezultat raportu chce otrzymać jako html, czy jako pdf. Jako web frameworku wykorzystałem nancyfx.

Jako bibliotekę, która zamienia html na pdf, wykorzystałem darmową bibliotekę, dostarczoną przez google, czyli wkhtmltopdf. Ta biblioteka jest dużo lepsza, od konkurencyjnej itextsharp ponieważ itextsharp na chwilę obecną nie radzi sobie z bardziej skomplikowanymi html'ami (w moim przypadku był to brak chart'ów o których wspomniałem w moim poprzednim wpisie).

Aby korzystać płynnie z wkhtmltopdf w .NET, skorzystałem z darmowej biblioteki Pechkin (link do githuba). Przykład wykorzystania pechkin'a z wkhtmltopdf można znaleźć na githubie ale również np. na blogu Ulises Reyes.
Ulises po za kilkoma przykładami użycia, informuje nas również o kilku ważnych aspektach korzystania z Pechkina:

  • It's trivial, here are some gotchas though:
  • GIF Images are not supported
  • Paths to images must be fully qualified as URLs (http://example.com/a/b/image.jpg) or file URIs (file://host/path)
  • Resources over HTTPS cannot be fetched through URLs. You can get around this by specifying the path as a file URIs.
  • References CSS files are supported, I found it easier to write inline CSS (classes in the same document enclosed in a style tag)
Ok. Mamy bibliotekę (napisaną w C), do zamiany html'a w pdf. Mamy adapter, aby wykorzystać tą bibliotekę w .NET. Brakuje nam jeszcze odpowiedniego kodu, aby wykorzystać to w naszej ukochanej Nancy. Na szczęście, natknąłem się na podobny wpis na blogu Shane Mitchell link. Tam wprawdzie była wykorzystywana inna biblioteka, ale nic nie stało na przeszkodzie, aby wszystko połączyć w jedną całość i utworzyć klasę, generującą odpowiedni widok:


using Nancy;
using Nancy.Responses;
using Nancy.ViewEngines;
using Pechkin;
using Pechkin.Synchronized;
using System.Drawing.Printing;
using System.IO;

namespace Nancy.MVC
{
    public static class ResponseAsPDF
    {
        public static Response RenderToPdfPechkin(NancyModule module, string viewName, dynamic model)
        {
            string content = GetPageContent(module, viewName, model);
            SynchronizedPechkin sc = new SynchronizedPechkin(new GlobalConfig().SetMargins(new Margins(0, 0, 0, 0))
                .SetDocumentTitle("Ololo").SetCopyCount(1).SetImageQuality(100)
                .SetLosslessCompression(true).SetMaxImageDpi(-1).SetOutlineGeneration(true).SetOutputDpi(-1).SetPaperOrientation(true)
                .SetPaperSize(PaperKind.Letter));
         
            byte[] pdfBuf = sc.Convert(new ObjectConfig().SetLoadImages(true)
            .SetPrintBackground(true)
            .SetAllowLocalContent(true)
            .SetScreenMediaType(true)
            .SetRunJavascript(true)
            .SetCreateExternalLinks(true), content);
         
            return new StreamResponse(() =>
            {
                var pdfOutput = new MemoryStream(pdfBuf);
                pdfOutput.Seek(0, SeekOrigin.Begin);
                return pdfOutput;
            }, "application/pdf");
        }
        private static string GetPageContent(NancyModule module, string viewName, dynamic model)
        {
            ViewLocationContext viewLocationContext = new ViewLocationContext()
            {
                Context = module.Context,
                ModuleName = module.GetType().Name,
                ModulePath = module.ModulePath
            };
            var rendered = module.ViewFactory.RenderView(viewName, model, viewLocationContext);
            var content = "";
            using (var ms = new MemoryStream())
            {
                rendered.Contents.Invoke(ms);
                ms.Seek(0, SeekOrigin.Begin);
                using (TextReader reader = new StreamReader(ms))
                {
                    content = reader.ReadToEnd();
                }
            };
            return content;
        }
    }
}