devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
4 minut

Generowanie plików PDF


13.01.2014

Wiele aplikacji wymaga generowanie dokumentów PDF – i dobrze, bo są fajne. Gdy jednak programista rzucony zostaje w całe wzburzone morze możliwych do zastosowania rozwiązań, bo nie jest już tak wesoło.

Kiedyś było prosto: brało się iTextSharp i wsio. Jednak od paru lat (?) licencja tego komponentu się zmieniła, i ja w sumie sam nie wiem kiedy można a kiedy nie można go używać oraz ile to kosztuje. Z niego korzystają inne dostępne rozwiązania, takie chociażby jak ciekawie wyglądający RazorPDF for MVC. A przecież nie chcemy licencji łamać, nie? Można rozejrzeć się także za płatnymi produktami, jak TallComponents.

Ja podczas researchu niechcący na blogu mojego brata, jeszcze z czasów konkursu Daj Się Poznać, natknąłem się na komentarz Gutka. Ot, przypadek! Gutek podpowiedział, że do generowania PDF można wykorzystać Reporting Services, nawet nie mając żadnego webserwisu czy sqla. Spróbowałem. Wyszło. Ba, okazało się to banalnie proste! Jest to o tyle fajne, że nie muszę niczego “rysować” w kodzie. Co jak co, ale do projektowania wyglądu PDFa designer to rzecz zdecydowanie pożądana (o czym można się zresztą przekonać zerkając na podlinkowany post).

A jak to osiągnąć?

Ja robiłem aplikację konsolową, ale pewnie podobnie proste jest to i w innych typach programów. Po pierwsze, dodałem do projektu New Item i z zakładki “Reporting” wybrałem Report Wizard:

clip_image001

Sam wizard nie jest banalny jeśli nie miało się z tym wcześniej do czynienia, ale na pewno każdy go bez większych problemów ogarnie w paręnaście minut. Wystarczy podać źródło danych (u mnie to był obiekt), porozrzucać kontrolki po powierzchni i tyle.

Dopiero później zaczęła się zabawa – dość ciężko było w necie znaleźć jakiś sensowny tutorial pokazujący jak, bez pośrednictwa zdalnej usługi renderującej raporty, stworzyć sobie PDFa. Oto co mi wyszło trochę metodą prób i błędów:

  1. plik definicji raportu (*.rdlc) oznaczamy we właściwościach jako “embedded resource” (to jest chyba nawet domyślna opcja); zamiast tego można wykorzystać fizyczny plik na dysku, ale nie lubię takich śmieci i w miarę możliwości staram się wrzucać do assembly wszystko co mi potrzebne; wada: zmiana w wyglądzie PDFa będzie wymagała rekomplikacji projektu
  2. szykujemy metodę, która nam ten plik wyłuska ze skompilowanego assembly (ja robię tak jak poniżej żeby nie babrać się w strukturze katalogów i przestrzeni nazw, IYKWIM):
  3. Stream get_report_definition()
    {
        Assembly assembly = Assembly.GetExecutingAssembly();
        var definitionName = assembly.GetManifestResourceNames()
            .Single(x => x.EndsWith("PdfDefinition.rdlc"));
     
        return assembly.GetManifestResourceStream(definitionName);
    }
    
  4. dodajemy referencje do Microsoft.ReportViewer.WinForms, System.Windows.Forms i System.XML
  5. implementujemy logikę generującą… PDFa, a cóżby innego:
  6. public byte[] GeneratePdf(PdfSource data)
    {
        using (var localReport = new LocalReport())
        {
            using (Stream reportDefinition = get_report_definition())
            {
                localReport.LoadReportDefinition(reportDefinition);
            }
     
            string reportDataSourceName = localReport.GetDataSourceNames()[0];
            var dataSource = new ReportDataSource(reportDataSourceName, new[] { data });
            localReport.DataSources.Add(dataSource);
     
            byte[] pdfBytes = localReport.Render("PDF");
     
            return pdfBytes;
        }
    }
    

    I już.

    PdfSource to klasa podana jako źródło danych w wizardzie odpalanym przy dodawaniu raportu do projektu.

    Ta konstrukcja:

    localReport.GetDataSourceNames()[0]
    

    pozwala mi uniezależnić się od nazwy wpisanej w wizardzie, bo od razu po jej wpisaniu zapomniałem jaka ona była.

    Obiekt z danymi podaję jako tablicę – bo tak trzeba (a pewnie w wielu przypadkach jest to nawet przydatne).

    Na koniec renderuję raport przekazując “PDF” jako oczekiwany format.

    Wsio. Działa. Teraz zostaje tylko uczynić ten plik “pięknym”, co jest zdecydowanie najtrudniejszą częścią całego zadania.

    Nie przegap kolejnych postów!

    Dołącz do ponad 9000 programistów w devstyle newsletter!

    Tym samym wyrażasz zgodę na otrzymanie informacji marketingowych z devstyle.pl (doh...). Powered by ConvertKit
Notify of
pawelek
pawelek

U nas przyjęło się, że raporty od dawien dawna były w htmlu. Potem było to drukowane poprzez drukarkę tworzącą pdf’y – novaPDF. Ale ostatnio zmieniono to używają wkhtmltopdf. Fajna biblioteka. Więc wygląd tworzysz sobie za pomocą htmla, a potem tylko to konwertujesz. Bierze pod uwagę css, wiec wsio może wyglądać tak jak lubisz :)

Robin
Robin

Fajny patent :)
A jak można wykoncypować rozwiązanie, które taki raport (PDF czy RS) wyśle od razu na zdefiniowaną lub domyślną drukarkę?

Tome

Ano .. rozwiązanie znane i używane (w jednym z projektów przy którym akurta nie pracowałeś :) ). Należy tylko pamiętać że przy wdrożeniu tegoż tam gdzie będzie to używane trzeba zainstalować SQL RS runtime.

Tam gdzie to używamy PDF ląduje w końcu jako dokument na faksie (ale poprzez bramkę SMTP).

Novakov
Novakov

Robiłem duży raport w reporting services (typu długi dokument z podsumowaniem zebranych danych, a nie prosta tabelka) i było z tym dużo problemu – puste strony, problem z utrzymaniem tabelki w kupie, więc ostrzegam przed używaniem RS do tego typu zastosowań (proste tabelki są ok).

Innym rozwiązaniem jest Apache FOP + XSL-FO. XSL-FO można generować przy pomocy XSLT albo Razora, a sam Apache FOP odpalić jako nowy proces albo przekonwertować na CLR przy pomocy IKVM.net

PiotrB

Po latach złych doświadczeń z Crystal Reports, później z Reporting Services, mój wybór padł na pdfsharp.
http://www.pdfsharp.net/

trackback

Generowanie plików PDF | Maciej Aniserowicz o programowaniu…

Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl…

jedmac

Do tej pory korzystałem z 2 rozwiązań – Syncfusion oraz ABCPdf. Jedno i drugie ma swoje zalety. Ogólnie przy generowaniu zwykłych pdfów, które zawierają tekst i obrazki nie ma żadnego problemu w jakimkolwiek rozwiązaniu. Schody zaczynają się w momencie, gdy musimy przygotować rozwiązanie z combobox’ami, list’ami itp. i dokument ma być później edytowalny ze zwykłego viewer’a tj. Adobe Reader/Foxit Reader. W momencie, gdy tworzymy taki dokument i wiemy, że się nie zmieni (tak jak wzorzec dla PIT np.) – jesteśmy w stanie to w miarę szybko osiągnąć. Natomaist jest jeszcze jedna sytuacja, z którą musiałem się zmierzyć – mieliśmy formularze… Read more »

Dario
Dario

Od lat niezmiennie polecam wkhtmlpdf. Htmla i Cssa zna chyba każdy :)
http://devpytania.pl/questions/3997/drukowanie-pdf-biblioteki/4009

Jackowy
Jackowy

PiotrB podał bardzo ciekawą alternatywę, pdfsharp. Pisze się znacznie wygodniej niż w iTextSharp, natomiast istnieje problem z czasem renderowania bardzo dużych tabel – problem, który istnieje od dłuższego czasu i nie został wciąż rozwiązany. Poza tym mankamentem, polecam :)

Adam

Przyłączam się do osób polecających PdfSharp. Nie jednego pdf’a w tym robiłem i uważam, że jest świetny i łatwy w użyciu :)

Jacek
Jacek

W ostatnim dużym projekcie gdzie trzeba było przetwarzać Pdf lub generować je z dokumentów Word miałem okazję korzystać z zestawu bibliotek Aspose. Niestety nie są tanie ale w porwaniu do wszystkiego innego z czego korzystałem pisanie na nich to czysta przyjemność. Człowiek przestaje walczyć z formatem a zaczyna z problemem.

GrzegorzL
GrzegorzL

Chyba zacznę pisać bloga, może wtedy zamiast od Gutka dowiedziałbyś się tego ode mnie ;P Sprawdziłem w starych projektach i już w 2008 z powodzeniem używałem tej metody korzystając z dołączonej do VS darmowej wersji CristalReports (generowane były pliki .rpt). Wtedy trzeba było przy deploymencie instalować runtime’a CR, co – jeśli dobrze pamietam – nie było takie łatwe, bo były jakieś problemy z wersjami bibliotek itp. Teraz z rdlc jest o wiele przyjemniej :)

Moja książka

Facebook

Zobacz również