Ú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ů.

  • Vytvoření vstupní šablony ve formátu
    .jrxml

  • Zkompilování šablony do .jasper souboru

  • Vložení vstupních dat do sestavy

  • Volání metod z JasperReports API pro tisk
    sestavy

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:

  • Parametr – v sestavě se označuje
    konstrukcí
    $P{název}
    . Má význam jedné
    konkrétní hodnoty, která se umístí někde v
    sestavě. Například si můžeme takto předat
    do hlavičky název firmy při tisku faktury.
    Do sestavy se vkládá jako instance Map<String,
    Object>
    s významem
    [název_proměnné, hodnota]

  • Field – v sestavě se označuje
    konstrukcí
    $F{název}
    . Má význam opakující se
    hodnoty. Například seznam zboží na faktuře.
    Do sestavy se vkládá jako instance třídy
    zděděné od JRDataSource. JasperReports pak
    seznam těchto hodnot opakuje pro jednotlivé
    řádky sestavy,

  • Variable – v sestavě se označuje
    konstrukcí $V{název}. Používá se pro různé
    výpočty prováděné přímo v sestavě.
    Například můžeme sečíst sumu zboží na
    faktuře. JasperReport obsahuje některé
    předdefinované výpočty, pro součet, průměr,
    největší hodnotu, nejmenší hodnotu atd.

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ů:

  1. V návrháři iReport vytvořit příslušnou
    proměnnou, pole, či parametr

  2. Použít element TextField pro umístění proměnné do
    sestavy na místo, kde se má zobrazit

  3. Při volání tisku z našeho programu vložit
    příslušná data.

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.

Po zkompilování sestavy a před tiskem
potřebujeme vložit do sestavy datový zdroj a
parametry. Jak již bylo zmíněno, datový zdroj
obsahuje jednotlivé řádky sestavy. Parametry
představují nějaké dodatečné informace, které
potřebujeme přenést na tiskový výstup. Jak bylo
zmíněno v předchozí kapitole, datový zdroj je
objekt zděděný od třídy JRDataSource. Parametry představuje
objekt java.util.Map,
kde vkladáme dovjice <String, Object> s významem [název
parametru, hodnota parametru]

K vyplnění sestavy parametry a datovým zdrojem
využíváme třídu

JasperFillManager

z balíku

net.sf.jasperreports.engine

V dokumentaci projektu JasperReport lze nalézt
různé modifikace metody fillReport(), která realizuje naplnění
sestavy daty. Mezi základní bezesporu patří
následující:

fillReport(JasperReport jasperReport, java.util.Map parameters, 

JRDataSource dataSource)

fillReport(java.io.InputStream inputStream, java.util.Map parameters,

JRDataSource dataSource)

fillReportToFile(JasperReport jasperReport,

java.lang.String destFileName, java.util.Map parameters,

JRDataSource dataSource)

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

  • title – titulek stránky. Pokud je sestava
    dlouhá na více stránek, zobrazuje se pouze
    na první.

  • pageHeader – hlavička stránky. Zobrazuje
    se vždy na vrchu každé stránky.

  • columnHeader – hlavička sloupečků
    tabulky. Bezprostředně obaluje sekci
    detail.

  • detail – sekce sestavy, kam umísťujeme
    hlavní obsah. Používá se pro zobrazení
    datové zdroje, neboť jako jediná umí
    vytvořit řádky pro hodnoty datového zdroje,
    které následně tiskne.

  • columnFooter – patička sloupců sestavy.

  • pageFooter – patička stránky.

  • lastPageFooter – patička poslední stránky
    sestavy. Zobrazuje se vždy pouze na
    poslední stránce. Důležitá je jedna její
    vlastnost. Pokud je tato sekce zobrazena,
    pageFooter se na poslední stránce nezobrazí
    na což je třeba myslet při návrhu.

  • summary – sekce používaná pro závěrečné
    součty, či shrnutí. Zobrazuje se pouze na
    poslední stránce sestavy. Přestože je
    uvedená v iReport až jako úplně poslední,
    na sestavě se zobrazí před columnFooter.
    Sekce se zobrazí bezprostředně za místem,
    kde končí tisk.

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.

Pokud jsme tiskli předchozí ukázky do formátu
PDF, pravděpodobně se chybně zobrazila slova s
diakritikou. Tento problém nastává proto, že v
sestavě je vždy nutné explicitně uvést, v jakém
kódování se má tisknout do PDF, přičemž defaultní
nastavení je CP1252.

Nastavení provedeme ve vlastnostech textového
políčka v návrháři iReport. Pravým tlačítkem myši
nad textovým políčkem vybereme z kontextového
menu “Vlastnosti”.
Na záložce “Font
dole nalezneme volbu “PDF
encoding
”. Nastavením volby “CP1250 (Central European)
zajistíme výstup v českém kódování.

iReport8

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.