Tisk z webových aplikací

Již nějakých dvacet let lze slýchat pojem "bezpapírová kancelář". Ovšem všechny ideje - od snahy naskenovat každý kus papíru po rozličné systémy elektronické výměny dokumentů - mají jedno společné: fungují přesně opačně. Rozšíření počítačů (a zejména levných a rychlých tiskáren) ve skutečnosti vedlo je zvýšení množství tištěných dokumentů.

Požadavek na tiskový výstup z webových aplikací je tedy stále častý. A protože jeho splnění není zcela jednoduché, může se stát vlčí jámou webových vývojářů. Poslední dobou se mi v mailu sešlo hned několik dotazů na toto téma, pročež jsem se rozhodl napsat tento přehledový článek. Měl by vám posloužit jako úvod do problematiky a návod k řešení nejtypičtějších problémů.

Tiskové požadavky lze v případě webových aplikací rozdělit do čtyř základních skupin. První je běžný tisk jednoduše strukturovaného dokumentu, kde jde o jeho obsah, nikoliv o přesné formátování. Například když si uživatel chce informace číst bez počítače a podobně. Druhou skupinu tvoří tiskové sestavy, kdy chceme mít nad vzhledem dokumentu jistou kontrolu, ale netrváme na absolutní věrnosti. Například tisk objednávek, faktur a dalších dokladů tohoto typu. Třetí skupina je případ, kdy je nutné nad tiskem mít detailní kontrolu. Například pokud je nutné doplňovat data do předtištěných formulářů a nebo chceme zajistit, aby výsledek měl z jiného důvodu přesné rozměry  pozicování. Poslední skupinu pak tvoří speciální případy, jako například tisk na pokladních tiskárnách a jiných specifických zařízení.

Úprava běžných stránek pro tisk

Všechny stránky by mělo být možné vytisknout. A prohlížeč se o to ochotně postará, stačí stisknout Ctrl-P. Bohužel, výsledek bývá mnohdy nevalný, layout rozhozený a použitelnost výtisku mizerná, zejména pokud si designér vymyslí bílé písmo na černém pozadí a podobné vylomeniny. Prohlížeče se sice obecně snaží stránky pro tisk upravovat, ale mnohdy je třeba jim pomoci.

V případě tisku je typicky vhodné některé elementy (hlavičku, menu…) vůbec nezobrazovat. Ačkoliv barevné tiskárny dnes nejsou tak exotickou záležitostí, jako před několika lety, vyplatí se ubrat na barvách. I když uživatel disponuje barevnou tiskárnou, černobílý tisk je zpravidla levnější a téměř vždy rychlejší, takže nenese-li barva nějakou podstatnou informaci, je lepší se bez ní obejít.

Existují v zásadě dvě cesty, jak tiskové verze stránek realizovat. První spočívá v tom, že vytvoříte samostatnou stránku s "verzí pro tisk", což je zejména v případě dynamicky generovaných webů celkem snadné. Toto řešení má několik výhod i nevýhod. Výhodou je, že uživatel přímo vidí, že se stránka změnila a jak se bude tisknout. Samostatnou stránku lze snadno využít i k jiným účelům, typicky třeba pro mobilní zařízení nebo nějak zdravotně postižené uživatele, kterým prospěje lepší čitelnost. Nicméně, pokud je váš web tak hrozný, že je nutné si zobrazit tiskovou verzi aby bylo možné ho rozumně číst, měli byste se nad sebou zamyslet. Nevýhodou tohoto přístupu je zejména duplikace obsahu a nestandardní ovládání. Duplikace obsahu může znamenat problém z hlediska vyledávačů a optimalizace pro ně, případně přivedení uživatelů z vyhledávače na tiskovou verzi a ztráta kontextu. Pokud se touto cestou vydáte, doporučuji odkazy na tiskovou verzi opatřit atributem rel="nofollow". Nestandardní ovládání je problém z hlediska použitelnosti, protože uživatel musí hledat nějaký specifický odkaz pro tisk na vašem webu (přičemž ani neví, zda tento existuje), místo aby prostě použil standardní funkci "tisk" svého browseru.

Mnohem vhodnější, než vytvářet samostatnou stránky pro tisk, je posle mého soudu využít vlastností CSS a vytvořit samostatný styl pro obrazovku a pro tisk.

Mějme například následující dokument:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

    <title>Untitled Page</title>

    <link rel="stylesheet" type="text/css" media="screen" href="screen.css" />

    <link rel="stylesheet" type="text/css" media="print" href="print.css" />

</head>

<body>

    <h1>Toto je nadpis</h1>

    <p class="screenonly">Tento text chci zobrazit na obrazovce, ale ne tisknout.</p>

    <p class="printonly">Tento text chci vytisknout, ale nezobrazovat ho na obrazovce.</p>

</body>

</html>

Atribut media elementu link zajistí, že pro zobrazení na obrazovce se použijí styly v souboru screen.css a pro tisk (a také náhlad před tiskem, takže můžete ladit bez nutnosti spotřebovat hromadu papíru) styly v print.css. Jinak oněch médií je víc, než jenom screen a print, ale dosud jsem se nesetkal s jejich praktickou implementací v žádném z běžných prohlížečů.

Výše uvedenou "výhybku" můžete realizovat i uvnitř souboru se stylem, který pak nalinkujete běžným způsobem. To se vám může hodit třeba při použití ASP.NET Themes, které vám nalinkují CSS automaticky. Obsah takového souboru může vypadat například následovně:

/* Tyto styly se použijí pro obrazovku */

@media screen {

    body {

        font-family: Arial, Helvetica, Sans-Serif;

        background-color: #cccccc;

        color: #000033;

    }

    h1 {

        color: #0000ff;

    }

    .printonly {

        display: none;

    }

}

/* Tyto styly se použijí pro tisk */

@media print {

    body {

        font-family: 'Times New Roman' , Serif;

        background-color: #ffffff;

        color: #000000;

    }

    h1 {

        color: #000000;

    }

    .screenonly {

        display: none;

    }

}

Uvedené styly také ukazují některé postupy, které budete patrně chtít použít:

  • Barvy: v zájmu šetření časem a tonerem tiskneme černé na bílém, včetně nadpisů, které jsou jinak barevně odlišené. Stejným způsobe doporučuji zachovat se i k odkazům.
  • Fonty: na obrazovce je čitelnější bezpatkové písmo, na papíře patkové. Obrazovka má malé rozlišení (myšleno DPI, hustotu bodů, typicky 72 dpi) a proto patky nezobrazí pořádně a oko spíš ruší. Tisk na papíře má velké rozlišení (typicky 300-600 dpi) a patky oko "vedou" při čtení.
  • Skrytí prvků: například menu a hlavičku budeme při tisku patrně chtít schovat.
  • Zobrazení prvků: je možné vytvořit speciální hlavičku pro tisk a skrýt ji naopak v obrazovkovém režimu.

Tvorba jednoduchých tiskových sestav

Druhou velkou skupinu tiskových úloh tvoří případy, kdy stránku vytváříme speciálně pro účely tisku. Typickým příkladem budiž vygenerovaná faktura. I zde nám pomůžou kaskádové styly.

Jako první se nám může hodit schopnost CSS specifikovat rozměry nejenom v pixelech a podobných jednotkách, ale také v milimetrech nebo centimetrech.  Následující konstrukce je tedy perfektně legální a většinou celkem funkční:

<div style="width: 10mm; height: 1cm; border: solid 5mm black">&nbsp;</div>

Prohlížeč se nám pokusí vyhovět, pokud mu v tom nebrání závažné okolnosti. Takovými okolnostmi může být třeba nastavení okrajů stránky a rozměry papíru. Pak prohlížeč stránku různým způsobem upraví tak, by se mu tisk na papír vešel. Každý prohlížeč to dělá jinak a mluvit do toho má co i uživatel, který může většinou ony tiskové okraje celkem pohodlně měnit. Na určení rozměrů tímto způsobem se nelze moc spolehnout a na určení pozice prvků už teprve ne.

Určení rozměrů je dobré v případě, že se spokojíte s přibližnou hodnotou. V duchu "tohle chci na orní půlce stránky a tohle někde na dolní", protože předmětnou stránku pak budete chtít napůl přeložit a rozříznout, například.

Pomocí kaskádových stylů můžeme také manipulovat se zalomením stránky. CSS vlastnosti page-break-before a page-break-after umožňují vynutit si zalomení stránky před nebo za daným elementem, nastavíme-li je na hodnotu always. Vlastnost page-break-inside nastavená na avoid pak prohlížeči říká, aby obsah elementu vytiskl pokud možno na jedné stránce (např. tabulku).

Nastavení těchto vlastností se v běžném zobrazení nijak neprojeví, já osobně mám ve zvyku používat za tímto účelem patřičně ostylovaný element hr, který ukáže na obrazovce rovnou čáru. Níže uvedený dokument mimochodem ukazuje ještě třetí možnost nastavení média pro styl, a to jako atribut elementu style.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

    <title>Untitled Page</title>

    <style type="text/css" media="print">

        hr {

            visibility: hidden;

            page-break-after: always;

        }

    </style>

</head>

<body>

    <p>Strana 1</p>

    <hr />

    <p>Strana 2</p>

    <hr />

    <p>Strana 3</p>

    <hr />

    <p>Strana 4</p>

    <hr />

    <p>Strana 5</p>

</body>

</html>

Velmi častým dotazem je: jak mohu pomocí CSS nebo jakkoliv jinak nastavit velikost okrajů stránky a hlavně vypnout a nebo alespoň nějak ovlivnit záhlaví a zápatí, které tam prohlížeč při tisku přidává? Bohužel, CSS ani žádná jiná technologie nic podobného neumí. Tato nastavení jsou plně v kompetenci uživatele a jeho prohlížeče a dokument do nich nemůže nijak civilizovaně zasahovat. Necivilizovaná řešení zahrnují použití ActiveX prvků a podobných věcí, které globálně modifikují nastavení prohlížeče. Mají společné především to, že nefungují moc spolehlivě, jsou specifické pro daný prohlížeč a obecně je s nimi víc problémů, než za kolik stojí.

Tisk z HTML je, ostatně stejně jako jeho zobazení na obrazovce, spíše přibližná záležitost založená na dobré vůli všech zúčastněných. Přesných výsledků lze dosáhnout pouze ve specifických případech, typicky odladěním pro jedno konkrétní nastavení v konkrétním prohlížeči a na konkrétní tiskárně. Dokumenty je tedy nutné připravovat tak, aby s tím počítaly a například nechávat dostatečně široké okraje (doporučuji 3 cm z každé strany).

Tvorba pokročilých tiskových sestav

Třetí možnost je, že potřebujete mít detailní kontrolu nad tím, jak bude výsledek vypadat, zejména pokud se týče rozměrů a pozicování. V takovém případě se potřebujete vymanit ze zajetí prohlížečea HTML. Jedinou spolehlivou metodou je dynamicky vygenerovat dokument, který se pak vytiskne externím programem nebo pluginem prohlížeče. Nejvhodnějším formátem je PDF – který byl ostatně přesně pro tento účel vytvořen. V úvahu připadá ještě XPS a DOC/DOCX.

XPS (XML Paper Specification) je formát od Microsoftu, který má za cíl konkurovat zastaralému PDF a je postavený nad OPS (Open Packaging Specification). Jeho nevýhodou je menší rozšířenost a obtížnější generování, nebo alespoň tedy mně se nepodařilo najít žádný jednoduchý způsob, jak XPS dokumenty generovat programově (jinak než skládat XML dokumenty).

Používat Office formáty (DOC/DOCX, XLS/XLSX) čistě pro tisk je tak trochu overkill, navíc u tiskového formátu je možnost editace spíše nevýhodou.

Osobně tedy používám PDF a pro jeho generování volně dostupnou knihovnu PdfSharp. Tato knihovna umožňuje na stránky kreslit podobným způsobem jako v GDI+ (existuje prý i nějaká podpora pro WPF, ale nemám s ní zkušenosti). Takto vygenerovaný dokument lze vytisknout zcela přesně, samozřejmě s omezeními danými vlastnostmi tiskárny.

Přímý tisk na speciální tiskárny

Poslední možností je tisk na speciální zařízení, jako jsou pokladí tiskárny, tiskárny čárových kódů a podobně. Komunikace s těmito zařízeními typicky neprobíhá pomocí standardního tiskového subsystému Windows. Místo toho je obvykle třeba jim posílat nějaké speciální instrukce přímo, nejčastěji přes sériový port. Komunikace s takovými tiskárnami z webového prohlížeče pak není z běžné aplikace možná. Jediným řešením je použít speciální program nebo vlastní ActiveX prvek, který tuto komunikaci zajistí.

  • Altairis
  • Nemesis
  • Microsoft MVP
  • IIS
  • ASP.NET