Úvod do problematiky tisku sestav

Tento článek vznikl jako projekt pod vedením doc. Ing. Pavla Herouta, Ph.D., Západočeská univerzita v Plzni, Fakulta aplikovaných věd, Katedra informatiky a výpočetní techniky
Copyright © Kamil Ježek, 2007

Table of Contents

1. Úvod
2. Instalace
1. Potřebné nástroje
3. Základní tisk
1. Úvod
2. První sestava
3. Sestava s daty
4. Podrobnosti procesu tisku
1. Kompilace sestavy
2. Datové zdroje
3. Vložení parametrů a datového zdroje
4. Tiskové výstupy
5. Shrnutí
5. Detaily návrhu stránky
1. Sekce sestavy
2. Kódování v PDF
3. Skupiny

Java je programovací jazyk, který obsahuje velice obsáhlé a bohaté knihovny funkcí pro různé oblasti využití. Pochopitelně existují i oblasti, pro které bychom metody v knihovnách Javy hledali marně. Na druhou stranu obliba zmíněného jazyka a velká základna programátorů zajišťuje, že pro mnoho problémů lze nalézt řešení v podobě dodatečných knihoven (reprezentovaných převážně .jar soubory). Přesně do tohoto kontextu lze zařadit tisk sestav.

Ve standardních třídách z Java API bychom metody pro jednoduchý tisk sestav hledali marně. Protože se však jedná o velmi potřebnou vlastnost pro mnoho aplikací, byla vytvořena knihovna funkcí nazvaná JasperReports. Systém je vyvíjen open-source komunitou a sám využívá další nástroje z produkce open-source vývojářů.

Díky velkému počtu uživatelů a vývojářů představuje JasperReports robustní nástroj, který se rychle vyvíjí a je k dispozici naprosto zadarmo. Jelikož je systém celý napsán v Javě a pracuje s XML dokumenty snadno se používá a je přenositelný napříč platformami.

JasperReports bohužel trpí nedostatkem dokumentace a to zejména v češtině. Z toho důvodu vznikla tato publikace. Neklade si za cíl vysvětlit všechny aspekty návrhu sestav a kompletně popsat API JasperReports, ale má čtenáře seznámit se základními postupy při návrhování sestav a volání tisku z Java programu.

Přestože text není enormně rozsáhlý, na konci čtení by uživatel měl být schopen vytvořit takovou sestavu, která bude použitelná v rozsáhlém programu. Ne snad proto, že je uvedený text geniálně napsán, ale právě díky jednoduchosti a elegantnosti systému JasperReports. Kapitoly uživatele provedou postupně od základů systému až ke složitějším sestavám. Po prostudování tohoto návodu by měl uživatel rozumět návrhu sestav a mít dostatek vědomostí na to, aby dokázat “objevovat” další možnosti tisku.

K tomu, abychom mohli využívat JasperReports pro tisk v Javě, potřebujeme instalovat jako první samotné knihovny systému JasperReports. Nejsnadněji je nalezneme na webových stránkách samotného projektu, které se nacházejí na adrese:

kde se snadno doklikáme ke stažení celého balíku knihoven. Nebo lze přímo použít odkaz na stažení:

Doporučuji stáhnout celý .zip soubor, který obsahuje nejen knihovnu JasperReports, ale také další podpůrné knihovny, které tento systém využívá. Dále ve staženém souboru nalezneme zdrojové kódy, několik ukázek a různé další soubory. Nás bude nejvíce zajímat v adresáři dist soubor JasperReports-xy.jar, kde xy je verze souboru a dále sada .jar souborů v adresáři lib. Samotné API pro tisk je obsaženo právě v souboru JasperReports-xy.jar, avšak ten pro svojí funkčnost využívá také další knihovny z adresáře lib. Nemusí být pravda, že právě náš program produkující tisk bude využívat všechny tyto soubory, ale je lepší je všechny do projektu začlenit, neboť nejsme autory JasperReports a nevíme při jaké příležitosti je ta která knihovna potřeba.

Aby náš program mohl používat stažené soubory, je třeba je začlenit do CLASSPATH. V každém vývojovém prostředí se tato volba nachází jinde avšak většinou se jedná o menu s názvem library, či project library. Alternativně můžeme nastavit do systémové proměnné CLASSPATH cestu do adresáře, kam jsme uložili stažené soubory.

Tímto bychom měli připraveny potřebné knihovny pro tisk. Nic nám nebrání v tom, začít tvořit první tiskové sestavy, avšak jak bude uvedeno dále, každá sestava se v JasperReports popisuje speciální šablonou ve formátu XML souboru. Pokud by nebyla jiná cesta, je možné si tuto sestavu napsat ručně v textovém editoru. Taková práce je však dosti nepraktická. Hodil by se grafický editor, kde bychom rovnou viděli, jak naše sestava bude vypadat a nemuseli se probírat nic neříkajícími XML tagy. Naštěstí takový systém existuje a jmenuje se iReport. Jelikož oba projekty, jak iReport tak JasperReport spolu úzce souvisejí, je možné grafický návrhář iReport stáhnout ze stejné webové adresy jako JasperReport. K dispozici je jak instalátor pro MS Windows, tak zdrojové soubory, nebo celý projekt iReport. Přičemž sám tento návrhář je napsán v Javě.

Tip

Mimo iReport je možné dohledat i jiné návrháře, ale mé zkušenosti říkají, že iReport je dostatečně dobrý o čemž svědčí i fakt, že je přímo doporučován ke stažení na webových stránkách společně s JasperReports.

Tip

Oba projekty, JasperReports a iReport jsou dostupné zdarma včetně zdrojových kódů

JasperReports je systém, který podle vstupní šablony produkuje výstup. Pro uživatele nabízí bohaté API pro tisk do různých formátů. Standardní Java API poskytuje rozhraní zaměřené zejména na tisk komponent JFC Swingu. V našich programech však častěji potřebujeme tisknout sestavy a v současné době se jeví JasperReports jako nejlepší volba.

Vytvoření každé sestavy se skládá ze čtyř základních kroků.

Vstupní .jrxml šablona není nic jiného, než XML soubor. Ten obsahuje sadu specifických elementů, které říkají, jak bude sestava vypadat, kde co bude umístěno a jaká data se kde vloží. Popis všech elementů a jejich význam zde však neuvádím, neboť sestavy budeme tvořit v iReport, který nás od ruční přípravy šablony naprosto odstíní.

Zkompilovaný .jasper soubor představuje serializovanou třídu, která vznikla ze vstupní šablony. Podle konkrétních požadavků na náš program, se můžeme rozhodnout, zda si naše sestavy předem zkompilujeme a program dále šíříme jenom s .japser soubory. Nebo můžeme sestavy kompilovat za běhu užitím metod z knihoven JasperReports. Program potom šíříme s .jrxml soubory. Druhou možnost zvolíme pravděpodobně v situaci, kdy čekáme, že se sestavy budou modifikovány za běhu programu.

compilation

Vstupní data sestavy jsou dvojího typu. Do sestavy vkládáme parametry a datový zdroj. Parametry se vkládají jako kolekce typu Map. Datový zdroj je objekt zděděný od JRDataSource. Různé typy datových zdrojů budou popsány dále. Zde postačuje informace, že datové zdroje mohou být například kolekce JavaBeanů, spojená s databází atd.

Rozhraní JasperReports pak obsahuje sadu metod, které umožňují vytvářet různé tiskové sestavy ať už výstupem na tiskárnu, nebo exportem do HTML, či PDF souboru.

Pro vytvoření první sestavy si otevřeme editor iReport. Volbou Menu / Nový dokument a založíme novou šablonu. Otevře se nám šablona s několika sekcemi (title, pageHeader, columnHeader atd.). V nástrojové liště nalezneme několik nástrojů pro kresbu čar, obdélníků, elips, či psaní textu. Podle své fantazie vytvořte nějakou sestavu a uložte.

iReport1

Nyní zbývá vytvořit program, který naší sestavu vytiskne. Otevřete si proto váš oblíbený editor. Zde připomeňme, že je potřeba nastavit prostředí tak, aby v CLASSPATH byl uveden soubor jasperreports-xy.jar a všechny soubory s adresáře LIB ve staženém balíku.

Pro správnou funkci je třeba importovat balík

import net.sf.jasperreports.engine.*;

A následně vložíme několik volání metod z JasperReports API pro tisk sestavy:

    public static void main(String[] args) throws JRException {
JasperReport jr = JasperCompileManager.compileReport(
"reports/example1.jrxml");
JasperPrint jp = JasperFillManager.fillReport(
jr, null, new JREmptyDataSource());
JasperPrintManager.printReport(jp, true);
}

Uvedený kód předpokládá, že jsme uložili sestavu se jménem example1.jrxml do podadresáře reports. Všiměte si také, že uvedené metody mohou vyhodit vyjímku JRException. Uvedený program nejprve metodou compileReport() zkompiluje vstupní šablonu. Následně metodou fillReport() vloží data a nakonec voláním printReport() tiskne. Hodnota null v metodě fillReport() říká, že nepředáváme žádně parametry do sestavy. Objekt JREmptyDataSource označuje, že nevkládáme žádný datový zdroj. Zde je dobré zdůraznit, že pokud naše sestava nevyužívá datový zdroj, ale tiskne pouze statický obsah (případně doplněný o parametry). Jako je tomu v našem případě, skutečně potřebujeme volat metodu s parametrem JREmptyDataSource. Kdybychom volali například s hodnotou null, výsledná sestava by byla prázdá (prázdný list). Třídy JasperCompileManager, JasperFillManager a JasperPrintManager obsahují velké množství statických metod pro kompilaci, plnění a tisk sestavy. Podrobný popis lze nalézt v dokumentaci. Nejvýznamnější uvedu v následujících částech textu.

V uvedeném příkladu je vidět, že jsme vyšli ze zdrojové .jrxml šablony a za běhu programu si šablonu zkompilovali (tedy .jasper soubor se fyzicky nikam neukládal, pouze se vygeneroval v paměti) a výsledek vytiskli na tiskárnu.

print4

V předchozí kapitole jsme si ukázali, jak vytvořit nejjednodušší statickou sestavu. Zpravidla však potřebujeme tisknout výstup dat našeho programu, což pochopitelně JasperReports umožňuje. Jelikož JasperReports je vhodný pro tisk sestav, obsahuje prostředky pro vkládání seznamů dat. Mimoto umožňuje sestavu různě parametrizovat vstupem doplňkových parametrů. Dále máme možnost provádět jednodušší výpočty přímo v sestavě. Abychom toho dosáhli, můžeme v šabloně použít celkem tři typy dat: parametry (parametrs), datové pole (fields), proměnná (variable) s následujícím významem:

Teď se může zdát situace mírně komplikovaná, ale práce se vstupními daty sestavy je vcelku jednoduchá. Vždy potřebujeme provést tuto posloupnost kroků:

Uvedený postup nejlépe ilustruje příklad. Představme si, že chceme tisknout jednoduchou sestavu s objednávkou zboží. V sestavě chceme zobrazit v titulku (měnitelný) název naší společnosti, následně vypsat objednané zboží s název a cenou a na konci výslednou cenu sečíst.

Nejprve si otevřeme iReport, kde založíme sestavu example2.jrxml. Vložíme si proměnné a parametry, které budeme potřebovat. Potřebujeme jeden parametr “firma”, dvě pole “zbozi”, “cena” a jednu proměnnou “soucet”, které si myší vhodně umístíme do formuláře. Pro vložení těchto políček do sestavy použijeme nástroj “Text field” nebo Textová “položka” v českém překladu iReport. Na následujícím obrázku je nástroj naznačen číslem “1”.

iReport2

Užitím tohoto nástroje vložíme postupně políčka $P{firma}, $F{zbozi}, $F{cena}, $V{soucet}, což odpovídá pravidlům uvedeným výše. K inspiraci může posloužit následující obrázek

iReport3

Následně u každé položky potřebujeme nastavit některé další vlastnosti. Zejména jaký datový typ bude zobrazovat. To provedeme stisknutím pravého tlačítka myši na políčku a z kontextového menu vybereme vlastnosti. Následně na záložce TextField nastavíme příslušný datový typ. U položek $P{firma} a $F{zbozi} můžeme ponechat implicitní typ String. U políček $P{cena} a $V{soucet} nastavíme pro jednoduchost typ Interger.

iReport4

Tím máme navrhnutou sestavu, kde bude která hodnota zobrazena a jaký datový typ se bude zobrazovat. Jak je vidět, report je rozdělen na několik sekcí. Prozatím nastavme, že informace o zboží budou v části “detail”, název firmy v části “title” a součet v části “summary”. To protože část “title” a “sumary” se zobrazuje pouze na první respektive poslední stránce. Naopak část “detail” je určena pro opakující se hodnoty, které JasperReports opakuje jako řádky sestavy.

Protože takto jsme si pouze vymezili, kde se budou naše hodnoty zobrazovat, musíme ještě příslušné proměnné vytvořit. To uděláme ikonou z nástrojové lišty označenou číslem 2 na obrázku výše. V okně, které se nám otevře můžeme vidět tři záložky: Fields, Parametrs, Variables, které odpovídají jednotlivým typům dat sestavy. Zde vytvoříme odpovídající parametr (Parameters) “firma”, pole (Fields) “zbozi” a “cena” a naposledy proměnnou (Variables) “soucet”. U součtu nastavíme, že proměnná má sčítat hodnotu “cena” v celém reportu, viz následující obrázek:

iReport5

Jak je vidět, nastavili jsme pro výpočet typ “sum” a výraz $F{cena}, což způsobí součet cen v celém reportu.

Nyní můžeme přistoupit k tvorbě programu, kterým vytvoříme sestavu. Předpokládejme, že zboží objednávky ukládáme v objektu JavaBean (Uveďme, že JavaBean je třída obsahující sadu instančních proměnných a k nim příslušné get a set metody) podle uvedeného kódu:

package jasperbook.example2;
public class Obchod {
private String zbozi;
private int cena;
public Obchod() {
}
public Obchod(String zbozi, int cena) {
this.zbozi = zbozi;
this.cena = cena;
}
public int getCena() {
return cena;
}
public void setCena(int cena) {
this.cena = cena;
}
public String getZbozi() {
return zbozi;
}
public void setZbozi(String zbozi) {
this.zbozi = zbozi;
}
public Object[] toArray() {
Object[] result = new Object[2];
result[0] = cena;
result[1] = zbozi;
return result;
}
}

To že jsme zvolili uložení hodnot v JavaBeanu není náhoda. Jak již bylo uvedeno výše, data vkládaná do sestavy jsou potomky objektu JRDataSource. Jeden z potomků se jmenuje JRBeanCollectionDataSource a umožňuje právě vkládat kolekce JavaBeanů jako datový zdroj sestavy. Obslužný program naší sestavy pak může vypadat takto:

        // Potřebujeme Mapu s parametry sestavy
Map<String, Object> params = new HashMap<String, Object>();
params.put("firma", "Nekupto");
List<Obchod> obchod = new ArrayList<Obchod>();
obchod.add( new Obchod("Pračka", 5000) );
obchod.add( new Obchod("Chladnička", 6000) );
obchod.add( new Obchod("Žehlička", 780) );
obchod.add( new Obchod("Sporák", 12000) );
JasperReport jr = JasperCompileManager.compileReport(
"reports/example2.jrxml");
JasperPrint jp = JasperFillManager.fillReport(jr,
params, // Vložíme parametry sestavy
// Vložíme datový zdroj
new JRBeanCollectionDataSource(obchod));
JasperPrintManager.printReport(jp, true);

Jak je vidět, vytvořili jsme si mapu s jedním klíčem, který je stejně pojmenovaný jako parametr v sestavě (tedy “firma”). Tuto mapu potom vkládáme jako parametry sestavy. Následně jsme vytvořili pole se čtyřmi objekty typu zboží, které se vložili jako data do sestavy. Objekt Obchod je napsán podle konvencí JavaBean, proto sestava “pozná”, že metodou getZbozi() ziská pole ($F{...}) zbozi a metodou getCena() pole ($F{...}) cena. Poté už pouze naskládá do řádků všechny položky předaného seznamu.

Po spuštení programu získáme následující výsledek:

print1

V předchozí kapitole jsme si ukázali, jak vytvořit jednoduchou sestavu v JasperReports. Jak je vidět, sestava by potřebovla pouze mírně vylepšit a již by byla použitelná pro reálnou aplikaci. V této kapitole si popíšeme podrobně jak probíhá celý proces tisku od napsání .jrxml šablony až po generování výstupu. Zejména se zaměříme na metody z API JasperReports, které používáme pro jednotlivé kroky procesu.

Než popíšeme podrobně možnosti tisku, uveďme obrázek, ilustrující kompletně proces generování sestavy. Z obrázku lze vyčíst, že před tiskem je třeba sestavu zkompilovat, vložit datový zdroj a parametry. Následně je možné generovat sestavu. Jednotlivé kroky budou popsány v následujíich kapitolách

print_flow

Jak bylo zmíněno výše, naši sestavu musíme po vytvoření zkompilovat. V JasperReports máme několik možností, jak toho dosáhnout, přičemž v různých situacích využijeme různé způsoby. Zejména nás bude zajímat, zda sestavu chceme kompilovat před spuštěním programu, nebo za běhu programu.

Ke kompilaci sestavy za běhu programu využijeme třídu

JasperCompileManager

Definovanou v balíku

net.sf.jasperreports.engine

Třída obsahuje celou řadu metod pro kompilaci. Nejčastěji však asi využijeme kompilaci ze zdrojového .jrxml souboru do objektu JasperReport, se kterým dále pracujeme.

JasperReport jreport = 
JasperCompileManager.compileReport("soubor.jrxml");

Někdy se může hodit zkompilovat šablonu do souboru na disk.

JasperCompileManager.compileReportToFile("soubor.jrxml"); 
// vytovří soubor "report_name.jasper"
JasperCompileManager.compileReportToFile("soubor.jrxml", "soubor2.jasper");
// vytovří soubor "soubor2.jasper"

U prvního příkazu je dobré si všimnout, že výsledný soubor se bude jmenovat stejně jako sestava (nikoliv jako jméno souboru ze šablonou). Jméno reportu nastavujeme při jeho vytváření, nebo později z menu Úpravy / Nastavení reportu v návrháři iReport.

Některé další modifikace kompilačních metod lze dohledat v JavaDoc.

Pokud z nějakého důvodu potřebujeme kompilovat sestavu předem, máme celkem dvě možnosti. Sestavu přímo zkompilujeme z návrháře iReport z menu Sestavit / Kompilovat, nebo ikonou z nástrojové lišty. V tomto případě nám iReport vytvoří soubor .jasper pojmenovaný stejně jako .jrxml šablona. Výhodné také je, že iReport zobrazí případné chyby v sestavě (např. nesprávné datové typy, nebo použití neexistujících proměnných). Při tvorbě nové sestavy využijeme s výhodou tuto možnost pro ladění, neboť se často stane, že některé proměnné přiřadíme nesprávný datový typ, či ji zapomeneme definovat.

Pochopitelně můžeme metody z API libovolně kombinovat. Pro inspiraci uvedeme následující kód, který kompiluje šablonu v souboru pouze pokud kompilovaná verze neexistuje a nebo je staršího data.

    public static String compile(File sourceFile, File compFile) 
throws JRException {
// Pokud zkompilovaný soubor neexistuje,
// Nebo je starší než zdrojový,
// zkompiluj. Jinak použij již zkompilovaný
if (!compFile.exists() ||
compFile.lastModified() < sourceFile.lastModified()) {
JasperCompileManager.compileReportToFile(sourceFile.getPath(),
compFile.getPath());
}
return compFile.getPath();
}

Uvedený kód v našem programu zajistí, že se soubor zkompiluje kdykoliv je šablona aktualizována aniž bychom museli program restartovat. A naopak není zbytečně kompilována při každém volání tisku a běh programu tím zbytečně nezpomaluje.

Pokud chceme mít jistotu, že máme k aktuální verzi našeho programu zkompilovanou poslední verzi sestavy, máme možnost využít task utility ANT, kterým si sestavu zkompilujeme. Podrobnosti k utilitě ANT lze nalézt v její dokumentaci. Potřebný task je definován v balíku:

net.sf.jasperreports.ant

kde nalezneme třídu:

JRAntCompileTask

Následně můžeme vytvořit naší úlohu pro kompilaci v souboru build.xml například takto:

<taskdef name="jrc" 
classname="net.sf.jasperreports.ant.JRAntCompileTask">
<classpath>
<fileset dir="/lib">
<include name="**/*.jar"/>
</fileset>
</classpath>
</taskdef>
<target name="-pre-compile">
<mkdir dir="${basedir}/reports"/>
<jrc srcdir="${basedir}/reports"
destdir="${basedir}/reports" >
<include name="**/*.jrxml"/>
</jrc>
</target>

V první části ukázky si vytvoříme nový task pojmenovaný “jrc”, který následně voláme pro kompilaci sestavy.

V sestavě, kterou jsme již vytvořili, jsme si předvedli, jak vložit datový zdroj sestavy. Přičemž jsme využili JRBeanCollectionDataSource, což je jeden ze zástupců datových zdrojů v JasperReports.

Jako první upřesníme, jak fungují datové zdroje sestavy. Datový zdroj vždy představuje seznam nějakých hodnot. Může se jednat o kolekci, či pole, nebo třeba databázovou tabulku. My pak v sestavě určujeme, jak se zobrazí jeden řádek a JasperReports vytiskne tolik řádek, kolik jich je ve zdroji.

Datové zdroje jsou v JasperReports definovány v balíku:

net.sf.jasperreports.engine

a jejich základem je interface

JRDataSource

Pokud chceme, můžeme uvedené rozhraní implementovat a vytvořit si vlastní datový zdroj. V JasperReports API však existuje mnoho tříd implementujících zmíněné rozhraní, s kterými si jistě dlouhou dobu vystačíme. K nejzajímavějším implementacím patří následují (ovšem v JavaDoc lze najít i další):

Často používaný datový zdroj, který přebírá jako parametr pole tříd splňujících konvence JavaBean

JRBeanArrayDataSource(java.lang.Object[] beanArray)

Podobný datový zdroj jako předchozí, ovšem místo pole přebírá kolekci (java.util.Collection) JavaBeanů

JRBeanCollectionDataSource(java.util.Collection beanCollection)

Následující datový zdroj je důležitý, pokud vytváříme “statickou” sestavu. Tedy sestavu s pouze grafickými prvky a případně s několika parametry. Potom se totiž nabízí předpoklad, že sestava bez dat nepotřebuje datový zdroj. Pokud bychom namísto datového zdroje poslali hodnotu null, vytiskne se prázdný list, což je chyba, která se špatně hledá. Proto, pokud tiskneme prázdnou sestavu, musíme vložit následující prázdný datový zdroj:

JREmptyDataSource()

Často náš program pracuje s daty z databáze, která můžeme přímo poslat do sestavy jako objekt java.sql.ResultSet (což je objekt získaný jako výsledek SQL dotazu přes rozhraní JDBC) následujícím způsobem:

JRResultSetDataSource(java.sql.ResultSet rs)

U desktopové aplikace často zobrazujeme data v objektu JTable, jenž využívá model javax.swing.table.TableModel. Tisk takových dat je stejně snadný, neboť existuje datový zdroj, který pracuje přímo s modelem tabulky:

JRTableModelDataSource(javax.swing.table.TableModel model)

V návrháři iReport, lze v menu “Data” nalézt volbu “Dotaz reportu”. V tomto menu, můžeme nastavit datový zdroj přímo v sestavě. Například můžeme vložit přímo SQL dotaz, který naplní data sestavy. Tuto možnost však nedoporučuji příliš používat ve větších aplikacích, neboť ztratíme návaznost mezi aplikací a sestavou. Lepší se jeví vkládat datové zdroje z aplikace a případné změny v aplikaci se snadněji přenesou i do sestav.

JasperReports obsahuje poměrně bohaté API pro různé tiskové výstupy. Neomezuje se pouze na výstup na tiskárnu, ale umožňuje produkovat i další formáty.

Třídy pro tisk nalezneme v balíku

net.sf.jasperreports.engine

Výstup na tiskárnu zajišťuje třída

JasperPrintManager

ve které nalezneme metody tisknoucí buď celý dokument, nebo jeho jednotlivé stránky. Zde uvedeme přehled základních metod:

Následující tři metody tisknou celou sestavu ze vstupního proudu, z objektu JasperPrint a nebo ze souboru na disku.

printReport(java.io.InputStream inputStream, boolean withPrintDialog)
printReport(JasperPrint jasperPrint, boolean withPrintDialog)
printReport(java.lang.String sourceFileName, boolean withPrintDialog)

Obdobně můžeme tisknout pouze konkrétní stránku sestavy, kde zadáváme jako parametr číslo stránky

printPage(java.io.InputStream inputStream, int pageIndex, 
boolean withPrintDialog)
printPage(JasperPrint jasperPrint, int pageIndex,
boolean withPrintDialog)
printPage(java.lang.String sourceFileName, int pageIndex,
boolean withPrintDialog)

Nebo můžeme tisknout sekvenci stránek zadáním počáteční a koncové stránky

printPages(java.io.InputStream inputStream, int firstPageIndex, 
int lastPageIndex, boolean withPrintDialog)
printPages(JasperPrint jasperPrint, int firstPageIndex,
int lastPageIndex, boolean withPrintDialog)
printPages(java.lang.String sourceFileName, int firstPageIndex,
int lastPageIndex, boolean withPrintDialog)

Jelikož se JasperReports neomezuje pouze na výstup na tiskárnu, můžeme nalézt metody pro výstup do HTML či PDF a to ve třídě

JasperExportManager

K dispozici máme metody pro tisk do HTML

exportReportToHtmlFile(JasperPrint jasperPrint, java.lang.String destFileName)
exportReportToHtmlFile(java.lang.String sourceFileName)
exportReportToHtmlFile(java.lang.String sourceFileName, java.lang.String destFileName)

tisk do PDF

exportReportToPdf(JasperPrint jasperPrint)
exportReportToPdfFile(JasperPrint jasperPrint, java.lang.String destFileName)
exportReportToPdfFile(java.lang.String sourceFileName)
exportReportToPdfFile(java.lang.String sourceFileName, java.lang.String destFileName)
exportReportToPdfStream(java.io.InputStream inputStream, java.io.OutputStream outputStream)
exportReportToPdfStream(JasperPrint jasperPrint, java.io.OutputStream outputStream)

V předchozích kapitolách jsme si ukázali různé možnosti kompilace, vytvoření datového zdroje a tiskové výstupy. V této kapitole si prakticky ukážeme, jak používat zmíněné metody. Uvedeme postupně všechny kroky, jak následovali za sebou v předchozích kapitolách. K výstupu využijeme soubor example3.jrxml, který jsme již vytvořili dříve.

Uvedeme různé varianty metod, které lze podle potřeby kombinovat.

Jako první ukažme kompilaci. Uvedeme příkazy pro kompilaci do objektu JasperReport a do souboru .jasper. Příklady jsou ilustrační, v praxi pochopitelně zvolíme jenom jednu volbu.

//kompilace do objektu JasperReport
JasperReport jreport = JasperCompileManager.compileReport(
"reports/example3.jrxml");
//kompilace do souboru. Výsledný soubor má jméno shodné
//se jménem reportu
JasperCompileManager.compileReportToFile("reports/example3.jrxml");
// kompilace do jiného souboru
JasperCompileManager.compileReportToFile("reports/example3.jrxml",
"reports/compiled3.jasper");

Následně si vytvoříme datový zdroj. Přeskočíme JRCollectionBeanDataSource, neboť byl použit v kapitole 3.3 Sestava s daty. Dále přeskočíme JREmptyDataSource, neboť jeho použití je zřejmé.

V dalším textu předpokládejme, že máme k dispozici následují dvě metody vracející kolekci a pole objektů zboží:

List<Obchod> obchodCollection();
Obchod[] obchodArray()

Datový zdroj pro pole JavaBeanů získáme následujícím voláním:

JRDataSource jrsource = new JRBeanArrayDataSource(obchodArray());

Dále si ukážeme jak získat datový zdroj z databáze. Příklad bude pouze ilustrativní, neboť k funkční verzi bychom potřebovali ovladač k databázi, nainstalovaný databázový systém a vytvořenou tabulku zboží. Doufejme, že pro čtenáře, který je seznámen s prací s databázemi v Javě, bude ukázka dostatečně ilustrativní, aby byl schopen podle ní vytvořit funkční kód. Ostatní odkažme na další literaturu, kde lze dohledat podrobnosti práce s databázemi v Javě.

Tento datový zdroj předpokládá, že položky $F{...} v sestavě budou pojmenovány stejně jako sloupečky v tabulce v databázi.

// Datový zdroj z objektu ResultSet
Connection con = ...; //V reálné aplikaci napojení na databázi
Statement stm = con.createStatement();
ResultSet rs = stm.executeQuery("Select * from obchod");
JRDataSource jrsource = new JRResultSetDataSource(rs);

Nakonec si ukážeme jak využít data z tabulky JTable. Předpokládejme použití základního DefaultTableModel modelu.

Nejprve si mírně upravíme třídu Obchod, abychom mohli získat její atributy jako pole, doplněním metody:

    public Object[] toArray() {
Object[] result = new Object[2];
result[0] = cena;
result[1] = zbozi;
return result;
}

Následně již vytvoříme objekt DefaultTableModel

DefaultTableModel model = new DefaultTableModel( new Object[] {"zbozi", "cena"}, 0);
for (Obchod zbozi: zboziCollection()) {
model.addRow(zbozi.toArray());
}
jrsource = new JRTableModelDataSource(model);

Vytvořený model jednoduše využijeme pro přípravu datového zdroje. Tento datový zdroj předpokládá, že položky $F{...} se budou jmenovat podle sloupců tabulky.

jrsource = new JRTableModelDataSource(model);

Po zkompilování sestavy a připravení datového zdroje se dostáváme k naplnění sestavy parametry. Tato operace je opravdu jednoduchá. Nejprve si však připravme pomocnou metodu pro parametry sestavy:

    public Map<String, Object> params() {
//Naplnění daty
Map<String, Object> params = new HashMap<String, Object>();
params.put("firma", "Nekupto");
return params;
}

Nyní naplníme data

// Vyplnění a uložení do objektu JasperPrint
JasperPrint jprint = JasperFillManager.fillReport(jreport, params(), jrsource);
// Vyplnění sestavy a uložení do souboru
JasperFillManager.fillReportToFile(jreport, "reports/filled.dat", params(), jrsource);

Jako poslední můžeme sestavu tisknout. Výstup může být do různých formátu. Jako vstup využijeme objekt JasperPrint, nebo vygenerovaný soubor filled.dat.

Nyní máme vše připravené no pro tisk a můžeme si ukázat několik různých možností, jak tisknout do různých formátů. Začněme výstupem na tiskárnu:

//Různé spůsoby tisku celého dokumentu
JasperPrintManager.printReport(jprint, true);
// Tisk pouze první stránky (stránky jsou číslovány od nuly
JasperPrintManager.printPages(jprint, 0, 0, true);
// Tisk pouze jedné stránky
JasperPrintManager.printPage(jprint, 0, true);

Následuje ukázka, jak vytisknout sestavu do HTML souboru. Po otevření výsledné HTML stránky budeme zřejmě mírně zklamáni, neboť grafika (zejména obrázek nákupního košíku) nedopadla dobře. S tím však bohužel nic neuděláme a musíme se spokojit s konstatováním, že JasperReports (alespoň současná verze) špatně exportuje grafiku do HTML formátu.

//Tisk do HTML
JasperExportManager.exportReportToHtmlFile(jprint,
"reports/example3.html");

Jako poslední si ukážeme jak jednoduše exportovat do PDF souboru

//Tisk do PDF
JasperExportManager.exportReportToPdfFile(jprint,
"reports/example3.pdf");

Při tvorbě naší první sestavy si bylo možné všimnout, že iReport zobrazuje stránku rozdělenou na několik sekcí. Význam některých je možné intuitivně odhadnout z názvu, avšak je dobré znát jejich přesný význam.

Nejprve si povíme něco málo o nastavení jednotlivých částí sestavy. Konfiguraci sekcí vyvoláme z menu Pohled / Sekce, případně z nástrojové lišty ikonou Sekce a nebo z kontextového menu kliknutím pravým tlačítkem myši kdekoliv v sestavě a výběrem Vlastnosti sekcí. Otevře se nám dialogové okno

iReport6

V dialogovém okně nalezneme všechny sekce sestavy. Můžeme nastavit číselně výšku (Band Height), zda se může rozdělit při přechodu na další stránku (Split allowed) a nakonec můžeme definovat za jakých okolností se vytiskne (podotkněme, že implicitně se sekce tisknou vždy). Pokud chceme, vložíme do okénka libovolnou podmínku zapsanou v jazyce Java, která musí vracet hodnotu Boolean. Na základě jejího vyhodnocení se daná sekce vytiskne, nebo nevytiskne. Zde poznamenejme, že JasperReports skutečně umí vyhodnocovat pouze objekty a nikoliv primitivní datové typy. Proto následující podmínka je pro JasperReports chybná

$P{text}.equals("Jasper Reports"); 
// vrací true, nebo false - primitivní datové typy

neboť vrací primitivní datový typ. Problém jednodušše napravíme vytvořením objektu

new Boolean($P{text}.equals("Jasper Reports")); // v pořádku

Dále si také všimněme, že můžeme kombinovat konstrukce jazyka Java s proměnnými JasperReports. Proto jsou uvedené texty možné.

Nyní se již můžeme podívat na konkrétní význam jednotlivých sekcí a zejména na to, kdy a kde se tisknou

Pro ilustraci si vylepšíme šablonu z našeho předchozího příkladu. Nová sestava může vypadat například takto:

iReport7

Využili jsme všechny sekce reportu. Stejným programem jako v předchozím příkladu vytiskneme a získáme následující výstup.

print2

Dostali jsme očekávaný výsledek. V obrázku si všimneme, že opravdu sekce pageFooter chybí, neboť se místo ní zobrazila lastPageFooter. A část sumary se zobrazila ihned za koncem tiskového výstupu v sekci detail.

V mnoha případech se může stát, že chceme položky v sestavě seskupit podle nějakého kritéria. To JasperReport a návrhář iReport umožňuje. K těmto účelům lze nalézt v menu návrháře položku Pohled / Skupiny reportu. Alternativně lze stejnou volbu vyvolat z nástrojové lišty. V dialogovém okně můžeme vytvořit potřebný počet skupin a nastavit různé vlastnosti.

Ukažme si na jednoduchém příkladu, jak pracovat se skupinami. Dejme tomu, že chceme u zboží na výdejce uvádět skupinu DPH a navíc spočítat cenu za celou skupinu.

Ještě je třeba zdůraznit, že pokud chceme správné rozdělení do skupin, musí data do sestavy přijít seřazená. Jak bude uvedeno dále, v sestavě vždy uvádíme klíčovou hodnotu, která bude definovat skupinu. JasperReport potom postupně vypisuje řádky sestavy a pokud se změní ona klíčová hodnota, vytvoří novou skupinu. O vytvoření nové skupiny tedy rozhoduje pouze změna klíčové hodnoty z řádku na řádek. JasperReports není schopen řádky zpětně přeuspořádat, čili musíme data předem seřadit sami.

Jako první si vytvoříme rozšířenou třídu původní třídy Obchod, která bude uchovávat i DPH. Třídu doplníme navíc o komparátor porovnávající DPH. Klíčová hodnota pro rozdělení do skupin bude tedy DPH a jak již bylo řečeno, musíme data podle této hodnoty předem seřadit.

public class ObchodDph extends Obchod implements Comparable<ObchodDph> {
private int dph;
public ObchodDph(String name, int cena, int dph) {
super(name, cena);
this.dph = dph;
}
public ObchodDph() {
}
public int getDph() {
return dph;
}
public void setDph(int dph) {
this.dph = dph;
}
public int compareTo(ObchodDph o) {
return dph - o.getDph();
}
}

Program pro tisk zde již neuvedeme celý, neboť by se jednalo o opis již dříve uvedeného. Budeme pouze potřebovat vytvořit několik testovacích dat, data seřadit a vytisknout stejně jako předchozí ukázky.

// testovací data (výpis zkrácen)        
List<ObchodDph> obchod = new ArrayList<ObchodDph>();
obchod.add( new ObchodDph("Pračka", 5000, 19) );
obchod.add( new ObchodDph("Chladnička", 6000, 19) );
obchod.add( new ObchodDph("Žehlička", 780, 19) );
obchod.add( new ObchodDph("Sporák", 12000, 19) );
...
// data musíme seřadit
Collections.sort(obchod);
JRDataSource jrsource = new JRBeanCollectionDataSource(obchod);
// metody pro tisk vynechány - stejné jako v předhozích ukázkách

Nyní můžeme otevřít návrhář iReport a zdokonalit předchozí sestavu. Jako první vytvoříme novou položku (field) pro hodnotu DPH, $F{dph}. Následně můžeme vytvořit skupinu pojmenovanou například “dph”. Nezapomeneme vyplnit klíčovou hodnotu, která bude určovat skupinu (Group experession). K dispozici máme ještě několik dalších voleb, jejichž význam by měl být zřejmý z názvu. Viz obrázek

iReport9

Po potvrzení dialogu se v sestavě objeví dvě nové sekce - dphHeader a dphFooter. Do těchto nových sekcí vložíme hodnotu proměnné $F{dph}, abychom měli přehledně zobrazenu úroveň DPH. Dále vytvoříme proměnnou (variable) $V{dphSoucet}, která sečte cenu za DPH, kterou umístíme do dphFooter. Nastavíme “Calculation Type” na hodnotu “Sum” a “variable expression” na $F{cena}, čímž získáme součet cen. Aby se sčítala pouze hodnota konkrétní skupiny je třeba ještě nastavit “Reset type” pro hodnotu “Skupina” a “Reset group” pro hodnotu “dph”. Tím říkáme, že hodnotu proměnné bude resetovat (nulovat) skupina a konkrétně se bude jednat o skupinu pojmenovanou “dph” (to protože v sestavě můžeme použít více skupin).

iReport10

Nové hodnoty můžeme do sestavy uspořádat podle následující ukázky.

iReport11

Spuštěním programu získáme následující výsledek:

print3

Vidíme, že na sestavě se skutečně objevily dvě skupiny DPH pro 5% a 19% se součtem na konci obou skupin.