Udostępnij dokumentację na swojej stronie w postaci automatycznie generowanych PDF-ów. Z artykułu dowiesz się jak wygenerować PDF-a ze strony WWW i jak dostosować bibliotekę do obsługi polskich znaków.
Czytanie obszernych dokumentów na ekranie komputera, a już w szczególności w oknie przeglądarki internetowej, nie należy do zbyt wygodnych. Wynika to z wielu przyczyn, takich
jak właściwości monitorów (np. niska rozdzielczość, wynosząca tylko 72 dpi), zbyt szeroki układ tekstu czy chociażby źle dobrany kolor tła i czcionki na stronie i wiele innych. Można robić wszystko, aby treść na stronie była przedstawiona w sposób przystępny, jednak gdy tekstu jest za dużo, nic nie zastąpi papierowego wydruku.
Jeśli chcesz zaproponować użytkownikom swojej strony możliwość wydruku wybranych treści bądź też dokumentów (druków przelewów, faktur itd.), warto pokusić się o wygenerowanie
PDF-a, który zawsze będą mogli pobrać z serwera, otrzymać e-mailem czy też od razu wydrukować. Doskonale do tego celu nadaje się napisana w PHP darmowa biblioteka FPDF, której instalację oraz konfigurację omówimy w tym artykule.
Instalacja i przygotowanie do pracy
Zanim zaczniesz korzystać z biblioteki, musisz pobrać ją ze strony domowej projektu, dostępnej pod adresem http://www.fpdf.org.
Po pobraniu pliku z biblioteką, musisz ją rozpakować. Możesz w tym celu posłużyć się programem PowerArchiwer (do pobrania ze strony http://www.powerarchiwer.com).
Gdy to zrobisz, możesz zabrać się do pracy. Warto jednak wcześniej przygotować polskie czcionki, z których będziesz korzystać podczas konfigurowania własnej aplikacji.
Niestety, mimo że otrzymujesz FPDF wraz z gotowym zestawem czcionek, brak wśród nich czcionek z polskimi znakami. Na szczęście przygotowanie własnego zestawu nie jest skomplikowane:
- Ze strony http://www.fpdf.org/fr/dl.php?id=22 pobierz program, który umożliwi przekształcenie czcionek ttf na type1 oraz usunięcie znaków spoza tablicy ISO-8859-2.
- Rozpakuj archiwum z programem.
- Dla własnej wygody utwórz nowy katalog na dysku (nazwij go jak chcesz, np. czcionki).
-
Następnie przegraj do niego:
- czcionki z polskimi znakami, do których chcesz mieć dostęp w generowanych plikach PDF,
- plik {stala}.map{/stala} z katalogu font/makefont, odpowiadający
kodowaniu znaków, z którego będziesz korzystać (najlepiej, jeśli będzie to to samo kodowanie, w którym są zapisane dane używane do wygenerowania dokumentu). Może więc to być na przykład plik {stala}iso-8859-2.map{/stala}, {stala}cp1250.map{/stala} lub któryś z pozostałych,
pobrany niedawno program {stala}ttf2pt1.exe{/stala}.
- Uruchom teraz linię poleceń. W menu
Start wybierz opcję Uruchom i w nowym oknie wpisz cmd. Zatwierdź, klikając OK. - Teraz przejdź do katalogu, w którym znajdują się skopiowane przez ciebie czcionki. Możesz w tym celu wydać polecenie cd czcionki (gdzie czcionki to oczywiście twój katalog).
- Wpisz polecenie: {stala}ttf2pt1.exe -b -L iso-8859-2.map arial.ttf arial{/stala}. Oczywiście należy tu podać odpowiednie nazwy czcionek oraz nazwę pliku .map. Polecenie to spowoduje wygenerowanie dwóch plików o nazwach czcionki i rozszerzeniach
- {stala}afm{/stala} oraz {stala}.pfb{/stala}.
- Następnie tworzymy prosty skrypt, który wygeneruje odpowiednie pliki na potrzeby FPDF – patrz niżej.
- Wygenerowane przez skrypt pliki {stala}arial.php{/stala} oraz {stala}arial.z{/stala} (oczywiście zamiast arial wstaw nazwę wybranej przez siebie czcionki) skopiuj do katalogu
font znajdującego się w katalogu FPDF.
A oto wspomniany skryp. Pamiętaj o odpowiednim
ustawieniu nazwy czcionki i ścieżki do skryptu {stala}makefont.php{/stala}:
Zauważ, że poszczególne style czcionek (pochylony,
pogrubiony, podkreślony etc.) zawarte zostały w osobnych plikach .ttf.
Pierwszy PDF
Zaczniemy od najprostszego PDF-a, zawierającego
tylko jedną linijkę z napisem Witaj świecie:
AddPage();
$objPDF->AddFont(\'Arial\', \'\', \'arial.php\');
$objPDF->SetFont(\'Arial\', \'\', 15);
$objPDF->Cell(40, 40, \'Witaj świecie\');
$objPDF->Output();
?>
Co robi ten skrypt? Po kolei:
- Linia 2: dołącza bibliotekę FPDF do skryptu (pamiętaj
tutaj o odpowiednim ustawieniu ścieżki do pliki {stala}fpdf.php{/stala} – będzie on używany we wszystkich przykładach w tym artykule – ścieżka zależy oczywiście od tego, gdzie znajduje się u ciebie biblioteka; w naszym przypadku był to katalog fpdf, umieszczony w katalogu z prezentowanymi
skryptami). - Linia 4: tworzy obiekt klasy FPDF. To on jest używany podczas generowania plików PDF za pomocą biblioteki FPDF.
- Linia 5: dodaje nową stronę do dokumentu PDF. Jako że jest to pierwsze wywołanie metody AddPage, do dokumentu dodana zostanie pierwsza strona.
- Linia 6: dodaje utworzoną przez nas niedawno czcionkę.
- Linia 7: ustawia dodaną czcionkę jako bieżącą. Będzie ona od tej pory wykorzystywana do generowania tekstu w dokumencie PDF.
- Linia 8: dodaje komórkę tekstową o wymiarach 40 mm na 40 mm, zawierającą tekst Witaj świecie.
- Linia 9: generuje na wyjściu dokument PDF.
Dodatkowego wyjaśnienia wymaga jeszcze linia 7, ustanawiająca czcionkę użytą do generowania
tekstu. Zastosowana metoda przyjmuje trzy parametry, z których wymagany jest tylko pierwszy (drugi i trzeci są opcjonalne):
- rodzina czcionki, czyli inaczej mówiąc, jej nazwa,
- styl określający czy czcionka ma być pochylona,
pogrubiona bądź z podkreśleniem. W celu określenia stylu używa się kombinacji liter B (pogrubiona), I (pochylona) oraz U (podkreślona), - ostatnim parametrem jest rozmiar czcionki podany w punktach (jest to ta sama jednostka, jakiej używa się na przykład w edytorze tekstu).
Dwa pierwsze parametry funkcji {stala}AddFont(){/stala} są takie same jak w opisanej powyżej {stala}SetFont(){/stala}. Trzeci parametr to z kolei nazwa skryptu PHP opisującego czcionkę (plik ten został wygenerowany przy okazji czynności opisanych w punkcie Instalacja i przygotowanie do pracy).
Dokument wielostronicowy z nagłówkiem i stopką na każdej stronie
Kolejny skrypt jest już bardziej skomplikowany.
Odczytuje on długi tekst z pliku tekstowego, a następnie generuje na jego podstawie plik PDF, dodając na każdej stronie nagłówek z tytułem oraz z stopkę z numerem strony. Dla urozmaicenia, stopka i nagłówek będą zapisane pochyłą i pogrubioną
czcionką w kolorze niebieskim. A oto skrypt realizujący zadanie:
blnShowHeader) {
$this->SetTextColor(26, 65, 197);
$this->SetFont(\'Times\', \'bi\', 12);
$this->SetX($this->fw - $this->
rMargin - $this->GetStringWidth($this->strHeader));
$this->Cell(0, 0, $this->strHeader);
$this->Ln(20);
}
}
public function Footer() {
if ($this->blnShowFooter) {
$this->AliasNbPages();
$this->SetTextColor(26, 65, 197);
$this->SetFont(\'Times\', \'bi\', 12);
$this->SetY(-20);
$this->Cell(0, 10, \'Strona \'.$this->PageNo().\'/{nb}\');
$this->Ln(20);
}
}
public function setHeader($strHeader) {
$this->strHeader = $strHeader;
}
public function enableHeader() {
$this->blnShowHeader = true;
}
public function disableHeader() {
$this->blnShowHeader = false;
}
public function enableFooter() {
$this->blnShowFooter = true;
}
public function disableFooter() {
$this->blnShowFooter = false;
}
$strLorem = file_get_contents(\'lorem.txt\');
$objPDF = new MyPDF();
$objPDF->setHeader(\'Loerm Ipsum\');
$objPDF->AddFont(\'Arial\', \'\', \'arial.php\');
$objPDF->SetFont(\'Arial\', \'\', 10);
$objPDF->SetAutoPageBreak(true, 20);
$objPDF->AddPage();
$objPDF->MultiCell($objPDF->fw - $objPDF->
rMargin - $objPDF->lMargin, 7, $strLorem);
$objPDF->Output();
?>
Aby móc dodać do generowanych PDF-ów takie elementy jak nagłówek i stopka, należy utworzyć własną klasę dziedziczącą po klasie FPDF, dostarczającą implementacji metod Header() oraz Footer(). Metody te odpowiedzialne są właśnie za generowanie stopek i nagłówków na każdej stronie. FPDF wywołuje je, gdy nadchodzi taka konieczność, dzięki czemu znajdą się one na każdej stronie nowego dokumentu.
Można jednak zawsze wyłączyć te elementy, korzystając ze specjalnych zmiennych ({stala}$blnShowHeader{/stala} oraz {stala}$blnShowFooter{/stala}). W zapanowaniu nad wyglądem dokumentu pomaga również fakt, że tekst z nagłówka nie jest przypisany na stałe, lecz ustalany za pomocą metody {stala}setHeader(){/stala}.
Jak pewnie zauważyłeś, metodach {stala}Header(){/stala} oraz {stala}Footer(){/stala} użyto kilku nowych metod: {stala}SetTextColor(){/stala}, {stala}SetX(){/stala}, {stala}SetY(){/stala}, {stala}GetStringWidth(){/stala}, {stala}AliasNbPages(){/stala}, oraz {stala}Ln(){/stala}.
Metoda {stala}SetTextColor(){/stala} służy do wybrania koloru tekstu. Przyjmuje ona trzy parametry, będące liczbami z zakresu 0-255, określające składowe barwy koloru: czerwoną, zieloną oraz niebieską. Aby najlepiej dobrać kolor tekstu, możesz skorzystać np. z windowsowego Painta.
Metoda {stala}SetX(){/stala} określa pozycję elementu na osi X (oś X jest osią poziomą). W przypadku metody Header będzie to pozycja napisu tworzącego nagłówek. Wartość, którą przyjmuje ta metoda, domyślnie podawana jest w milimetrach (domyślne jednostki można zmienić w konstruktorze FPDF, jednak dla polskiego użytkownika wydają się one ustawione poprawnie).
Metoda {stala}SetY(){/stala} działa analogicznie, z tą różnicą, że określa pozycję elementu względem osi Y (pionowej).
Metoda {stala}GetStringWidth(){/stala} zwraca z kolei długość tekstu podanego jako parametr (długość ta jest zwracana w ustalonych w konstruktorze jednostkach, a więc w naszym przypadku – jako że korzystamy z konstruktora domyślnego, a więc i z domyślnych ustawień – w milimetrach).
Tak więc linia:
$this->SetX($this->fw - $this->rMargin - $this->GetStringWidth($this->strHeader));
pozwala na ustawienie pozycji napisu w nagłówku tak, aby znajdował się on z prawej strony. Dodatkowo użyta tu została zmienna {stala}$this->rMargin{/stala}, mająca wartość odpowiadającą szerokości prawego marginesu (analogiczna zmienna {stala}$this->lMargin{/stala} odpowiada szerokości lewego marginesu).
Metoda {stala}AliasNbPages(){/stala} nakazuje FPDF podmienić
występujący w napisach ciąg {stala}{nb}{/stala} liczbą wygenerowanych stron. Zauważ, że ciąg ten został użyty podczas generowania stopki.
Metoda {stala}Ln(){/stala} wstawia znak nowej linii oraz przesuwa karetę (wirtualny element wypisujący znaki) do jej początku. Działa więc analogicznie do symbolu \”\\n\”. Dodatkowo metoda ta przyjmuje jako parametr wysokość przerwy umieszczonej między liniami.
To już wszystkie nowe metody użyte wewnątrz klasy MyPDF. Czas skorzystać z gotowej klasy. Tworzymy jej obiekt:
$objPDF = new MyPDF();
Ustawiamy treść stanowiącą nagłówek:
$objPDF->setHeader(\'Loerm Ipsum\');
Wczytujemy oraz ustawiamy czcionkę:
$objPDF->AddFont(\'Arial\', \'\', \'arial.php\');
$objPDF->SetFont(\'Arial\', \'\', 10);
Informujemy FPDF o tym, że ma samodzielnie zadbać o dodawanie nowych stron, jeśli elementy przestają się już mieścić na stronie bieżącej:
$objPDF->SetAutoPageBreak(true, 20);
Dodajemy do dokumentu pierwszą stronę:
$objPDF->AddPage();
Dodajmy komórkę z napisem. Tym razem jednak używamy w tym celu metody {stala}MultiCell(){/stala}, która sama zadba o łamanie linii wychodzących poza stronę, a także zwraca uwagę na znak \”\\n\” napotkany w tekście:
$objPDF->MultiCell($objPDF->fw - $objPDF->
rMargin - $objPDF->lMargin, 7, $strLorem);
Metoda {stala}MultiCell(){/stala} przyjmuje od trzech do sześciu parametrów:
- szerokość linii tekstu (0, jeśli ma być automatycznie zwiększana aż do marginesów dokumentu),
- wysokość linii tekstu,
- tekst do umieszczenia w dokumencie,
- obramowanie – domyślna wartość to 0:
- 0 – brak ramki
- 1 – ramka dookoła tekstu wypisanego za pomocą metody MutliCell
- L – linia z lewej strony tekstu,
- R – linia z prawej strony tekstu,
- T – linia nad tekstem,
- B – linia pod tekstem
- wyrównanie tekstu w elemencie – domyślną wartością jest J:
- L – do lewej strony
- C – do środka,
- R – do prawej strony,
- J – wyjustowanie (wyrównanie do lewej i prawej strony)
- wypełnienie zawartości elementu tłem:
- 0 oznacza, że element ma być przeźroczysty (elementy umieszczone pod nim będą prześwitywały
pod tekstem), - 1 oznacza, że w przypadku ustawienia koloru wypełnienia (metoda {stala}SetFillColor(){/stala}, przyjmująca
parametry analogiczne do metody {stala}SetTextColor(){/stala}),
element ma zostać nim wypełniony – to, co znajdzie się pod elementem, nie będzie widoczne.
- 0 oznacza, że element ma być przeźroczysty (elementy umieszczone pod nim będą prześwitywały
Na koniec generujemy dokument PDF i wysyłamy go do przeglądarki.
$objPDF->Output();
Metodzie {stala}Output(){/stala} można przekazać dwa opcjonalne parametry:
- nazwę wygenerowanego pliku PDF – domyślna nazwa to {stala}doc.pdf{/stala},
- informację o tym, gdzie ma zostać wysłany wygenerowany dokument:
- I – wysyła dokument do przeglądarki – jest to wartość domyślna,
- D – wysyła dokument do przeglądarki, ale powoduje wyświetlenie okna z komunikatem do zapisania wygenerowanego pliku zamiast jego wyświetlenia,
- F – zapisuje wygenerowany plik w katalogu i do pliku podanego w pierwszym parametrze,
- S – zwraca zawartość wygenerowanego dokumentu jako napis (nazwa z pierwszego parametru jest w tym przypadku ignorowana).
Generowanie PDF-ów z BB kodu
W tej części artykułu wygenerujemy dokument PDF, posługując się BB kodem. Zaprezentowana poniżej klasa BB_PDF obsługuje następujące znaczniki:
- [b], [i] [u] – odpowiednio pogrubiający, pochylający
oraz podkreślający napis (używa się ich w następujący sposób: [b]tekst pogrubiony[/b]),
- [br] – wymuszenie przejścia do nowej linii,
- [img] – dodająca obrazek do dokumentu (używana w następujący sposób – {stala}[img]ścieżka_do_obrazka/obrazek.jpg[/img]{/stala})
- {stala}[url] – dodająca odnośnik do dokumentu (używana w następujący sposób [url http://adres.pl]nazwa[/url]{/stala})
Kod gotowego skryptu jest tym razem bardziej obszerny, trzeba bowiem parsować tekst i odpowiednio reagować na napotkane po drodze znaczniki:
false,
2 => false,
3 => false,
);
public function enableFooter() {
$this->blnShowFooter = true;
}
public function disableFooter() {
$this->blnShowFooter = false;
}
public function writeBB($strText) {
$arrTags = preg_split(\'/\[(.*)\]/U\', $strText, -1, PREG_SPLIT_DELIM_CAPTURE);
foreach ($arrTags as $intI=>$strData)
{
if ($intI % 2 !== 0) {
$this->parseTag($strData);
} else {
$this->parseText($strData);
}
}
}
private function parseTag($strTag) {
$strTag_ = explode(\' \', $strTag);
$strTag_ = $strTag_[0];
if ($strTag{0} === \'/\') {
$strTag_ = ltrim($strTag, \'/\');
}
$strMethod = \'execute\'.ucfirst(strtolower($
strTag_));
$objR = new ReflectionClass($this);
if ($objR->hasMethod($strMethod)) {
$this->$strMethod($strTag);
}
}
private function parseText($strText) {
if(!$this->blnInTag) {
$this->strTagContent = \'\';
$this->Write(5, $strText);
} else {
$this->strTagContent .= $strText;
}
}
private function executeB($strTag) {
if ($strTag{0} === \'/\') {
$this->setStyle(self::BOLD, false);
} else {
$this->setStyle(self::BOLD, true);
}
}
private function executeI($strTag) {
if ($strTag{0} === \'/\') {
$this->SetStyle(self::ITALIC, false);
} else {
$this->SetStyle(self::ITALIC, true);
}
}
private function executeU($strTag) {
if ($strTag{0} === \'/\') {
$this->SetStyle(self::UNDERLINE,
false);
} else {
$this->SetStyle(self::UNDERLINE,
true);
}
}
private function executeBr($strTag) {$this->Ln(5);
}
private function executeUrl($strTag) {
if ($strTag{0} === \'/\') {
$this->blnInTag = false;
$arrData = array_pop($this->
arrTagsData[\'url\']);
$this->setStyle(self::UNDERLINE,
true);
$this->SetTextColor(0,0,255);
$this->write(5, $this->strTagContent,
$arrData[1]);
$this->setStyle(self::UNDERLINE,
false);
$this->SetTextColor(0);
} else {
$this->blnInTag = true;
$arrData = explode(\' \', $strTag);
if (!isset($this->arrTagsData[\'
url\'])) {
$this->arrTagsData[\'url\'] = array();
}
array_push($this->arrTagsData[\'
url\'], $arrData);
}
}
private function executeImg($strTag) {
if ($strTag{0} === \'/\') {
$this->blnInTag = false;
$this->Image($this->strTagContent,
$this->GetX(), $this->GetY());
} else {
$this->blnInTag = true;
}
}
private function setStyle($intStyle, $blnEnable) {
$this->arrStyle[$intStyle] = $blnEnable;
$strStyle = \'\';
foreach ($this->arrStyle as $intSyle=>$
blnValue) {
switch ($intSyle) {
case 1: $strStyle .= $blnValue
? \'B\' : \'\'; break;
case 2: $strStyle .= $blnValue
? \'I\' : \'\'; break;
case 3: $strStyle .= $blnValue
? \'U\' : \'\'; break;
}
}
$this->SetFont(\'\', $strStyle, 10);
}
}
$strText = \'[b]Naglowek[/b][br][br][i]pochyly[/
i][br][u]podkreslony[/u][br]odnosnik [url http://www.internetmaker.pl]Internet
Maker[/url][br]Obrazek [img]im_okladka.jpg[/img][br]Mix:[br][b]Ala [u]ma [i]Kota[/i][/u][/b]\';
$objPDF = new BB_PDF();
$objPDF->AddFont(\'Arial\', \'\', \'arial.php\');
$objPDF->AddFont(\'Arial\', \'b\', \'arialbd.php\');
$objPDF->AddFont(\'Arial\', \'i\', \'ariali.php\');
$objPDF->AddFont(\'Arial\', \'bi\', \'arialbi.php\');
$objPDF->SetFont(\'Arial\', \'\', 10);
$objPDF->SetAutoPageBreak(true, 20);
$objPDF->AddPage();
$objPDF->writeBB($strText);
$objPDF->Output();
?>
Podany kod może wydawać się skomplikowany,
jednak w rzeczywistości używa on jedynie dwóch nowych metod biblioteki FPDF: {stala}Write(){/stala} oraz {stala}Image(){/stala}. Pierwsza z nich wypisuje fragment tekstu, druga wstawia do dokumentu obrazek.
Metoda {stala}Write(){/stala} przyjmuje od dwóch do trzech parametrów:
- wysokość napisu,
- tekst, który ma być wstawiony do dokumentu,
- adres URL, do którego ma prowadzić kliknięcie na wstawionym tekście – jest to parametr opcjonalny.
Metoda {stala}Image(){/stala} wstawia do dokumentu obrazki (w formacie JPEG lub PNG). Pamiętaj jednak, że obrazek ten musi znajdować się na tym samym serwerze, na którym uruchamiany jest skrypt – nie można podać tu np. adresu URL prowadzącego do obrazka. Metoda ta przyjmuje od trzech do siedmiu parametrów:
- nazwę pliku graficznego, który ma zostać wstawiony do dokumentu,
- pozycję X, w której ma się znaleźć lewy górny odnoróg obrazka w dokumencie,
- pozycję Y, w której ma się znaleźć lewy górny róg obrazka w dokumencie,
- szerokość obrazka,
- wysokość obrazka,
- format pliku graficznego (JPG, JPEG, PNG),
- adres URL, do którego ma prowadzić kliknięcie na obrazku.
Aktualną pozycję X oraz Y (czyli miejsce, gdzie na przykład ostatnio zostało zakończone wstawianie tekstu do dokumentu) można określić za pomocą metod getX() oraz getY().
A jak działa sam skrypt? Klasa BB_PDF za pomocą wyrażenia regularnego zamienia tekst na tablicę zawierającą na zmianę tekst oraz znacznik (array(\’tekst\’, \’znacznik\’, \’tekst\’, \’znacznik\’…)).
Następnie tablica ta jest przechodzona za pomocą pętli foreach i dla każdego jej elementu wykonywane jest odpowiednie działanie – jeśli jest to tekst i nie jest on częścią znacznika (np. adresem ze znacznika [url]), jest wyświetlany za pomocą metody {stala}parseText(){/stala}. Jeśli jest to znacznik, jego obsługa przekazywana jest metodzie {stala}parseTag(){/stala}. Metoda ta sprawdza, czy istnieje metoda o nazwie {stala}executeZNACZNIK{/stala} (czyli na przykład dla znacznika {stala}[url] – executeUrl(){/stala}) i w zależności
od tego uruchamia ją.
Dzięki temu dodanie obsługi kolejnego znacznika sprowadza się do dodania do klasy odpowiedniej
metody {stala}executeX(){/stala}.
Na koniec
To nie wszystkie możliwości FPDF-a, jednak nie sposób w tak krótkim artykule ich wszystkich
opisać. Poza tym, co zostało tu omówione, FPDF pozwala m.in. na rysowanie prostych figur geometrycznych. Warto poznać bliżej tę bibliotekę
i przekonać się, że samodzielne generowanie dokumentów w postaci PDF-ów nie musi wcale być trudne.