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:
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:
- 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
- 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):
- dodajemy referencje do Microsoft.ReportViewer.WinForms, System.Windows.Forms i System.XML
- implementujemy logikę generującą… PDFa, a cóżby innego:
Stream get_report_definition() { Assembly assembly = Assembly.GetExecutingAssembly(); var definitionName = assembly.GetManifestResourceNames() .Single(x => x.EndsWith("PdfDefinition.rdlc")); return assembly.GetManifestResourceStream(definitionName); }
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.
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 :)
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ę?
pawelek,
Prawda, z HTML/CSS byłoby jeszcze wygodniej (z reporting services już się trzeci dzień męczę żeby to wyglądało jak chcę), ale to ma się wszystko dziać automatycznie więc drukowanie mogłoby być jeszcze bardziej problematyczne.
Robin,
Drukowanie jest jakoś tam w C# wspierane, możesz zobaczyć tu http://sandeep-aparajit.blogspot.com/2008/05/how-to-print-filestring-in-c.html albo tu http://msdn.microsoft.com/en-us/library/aa287530(v=vs.71).aspx albo w jeszcze wiele innych miejsc :) sam tego nie robiłem
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).
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
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/
Generowanie plików PDF | Maciej Aniserowicz o programowaniu…
Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl…
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 HTML, które w zależności od kliknietych opcji rozszerzały się, bądź zwijały. Po kliknięciu przycisku “Send”, na podstawie formularza HTML renderowaliśmy formularz PDF ze wszystkimi interaktywnymi opcjami. Niestety, okazało się, że nie można w locie nadawać full-rights dokumentom, aby można je było później otworzyć w zwykłym view’erze. I tu są 2 opcje:
– albo rzeczywiście jako wymóg stawiamy klientowi posiadanie Adobe Acrobat
– albo korzystamy z całej infrastruktury firmy Adobe (z tego co pamiętam to był to kosmiczny koszt)
To taka mała dygresja na temat PDF :) Do zwykłych PDF’ów też jak najbardziej polecam Reporting Services.
Od lat niezmiennie polecam wkhtmlpdf. Htmla i Cssa zna chyba każdy :)
http://devpytania.pl/questions/3997/drukowanie-pdf-biblioteki/4009
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 :)
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 :)
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.
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 :)