Generowanie dokumentów PDF przy pomocy PHP

Autor: Marko Djukic
Data publikacji: 30.12.2003, 20:18 | Ostatnia modyfikacja: 05.11.2006, 17:46

Możliwość wygenerowania dokumentu PDF przy pomocy PHP to całkiem niezła rzecz. Dzięki temu artykułowi dowiesz się jednak, jak bez użycia jakichkolwiek modułów PHP go wykonać.



Wstęp

Ten artykuł jest skierowany do programistów PHP, którzy muszą zaimplementować generowanie dokumentów PDF bez użycia zewnętrznych bibliotek typu PDFlib (często niedostępnych z powodu ograniczeń licencji, lub braku funduszy). Nie pokaże on wszystkiego, a tylko same podstawy, tworzące jednak doskonałą platformę na dalsze eksperymenty. PDF posiada ogromne możliwości i z oczywistych względów nie mogą być one zawarte w tak krótkim dokumencie. Jeśli potrzebujesz więcej, niż jest tu opisane, możesz poszukać bardziej kompletnych materiałów, np. świetnego skryptu Olivera Platheya zwącego się klasą FPDF (http://fpdf.org), na której bazuje ten tekst. Możesz także spróbować napisać coś bez żadnej pomocy, posiłkując się dokumentacją formatu PDF (uwaga: zajmuje ona 1172 strony!).

Wymagana jest podstawowa znajomość klas w PHP. Wiedza o budowie pliku PDF nie jest potrzebna, gdyż wszystko jest tutaj wyjaśnione.

Do góry

Rzut okiem

Pliki PDF są w zasadzie zwykłymi dokumentami tekstowymi zawierającymi odpowiednie znaczniki opisujące, co się powinno stać z obiektami dokumentu typu tekst, czy obrazki. Oznacza to, że każdy, kto zna budowę ich budowę, jest w stanie je bardzo szybko utworzyć. Artykuł ten pokaże jej podstawy, co pozwoli także Tobie na stworzenie twojego własnego pliku PDF.

Do góry

Czego się nauczysz?

Na końcu pierwszej części tego artykułu będziesz w stanie utworzyć prostą klasę PDF potrafiącą:

Do góry

Co jest potrzebne?

Potrzebować będziesz w pełni sprawnego PHP (przedstawiony tu kod działa zarówno z wersją 4, jak i 5), oraz serwera WWW do wyświetlenia gotowego dokumentu PDF. Przyda się również jakiś program do ich podglądu (np. Acrobat Reader, lub XPDF). Nie jest wymagane także posiadanie jakiejkolwiek zewnętrznej biblioteki wkompilowanej, bądź skryptu. Wszystko jest zawarte w kodzie prezentowanym przez artykuł.

Do góry

Jak to działa?

Najlepszym wyjściem jest upakowanie kodu w klasę. Zapewni to później większą wygodę użycia. Główne (publiczne) metody zapewnią dostęp do głównych operacji na dokumencie PDF: tworzenia, dodawania stron, ustawiania czcionki, dodawania tekstu, aktywacji kompresji, oraz wyświetlania pliku. My omówimy sobie wszystkie metody, a także podstawy języka PDF, po czym upakujemy je w klasie.

Do góry

Inicjowanie pól klasy

Będziemy potrzebować kilku pól klasy do przechowywania informacji wyjściowych, stron, obiektów, ustawień itp. Oto ich lista wraz z komentarzami. W następnych fragmentach kodu zobaczysz, jak są one dokładnie wykorzystywane, teraz natomiast po prostu zapoznaj się z nimi.

var $_buffer = ''; // Bufor przechowujący dokument PDF
var $_state = 0; // Stan aktualnej strony
var $_page = 0; // Numer aktualnej strony
var $_n = 2; // Numer aktualnego obiektu
var $_offsets = array(); // Tablica indeksów obiektów
var $_pages = array(); // Tablica stron
var $_w; // Szerokość strony w punktach
var $_h; // Wysokość strony w punktach
var $_fonts = array(); // Tablica użytych czcionek
var $_font_family = ''; // Typ użytej czcionki
var $_font_style = ''; // Styl użytej czcionki
var $_current_font; // Tablica z danymi używanych czcionek
var $_font_size = 12; // Aktualne wielkość czcionki w punktach
var $_compress; // Czy kompresować dokument?
var $_core_fonts = array(
'courier' => 'Courier',
'courierB' => 'Courier-Bold',
'courierI' => 'Courier-Oblique',
'courierBI' => 'Courier-BoldOblique',
'helvetica' => 'Helvetica',
'helveticaB' => 'Helvetica-Bold',
'helveticaI' => 'Helvetica-Oblique',
'helveticaBI' => 'Helvetica-BoldOblique',
'times' => 'Times-Roman',
'timesB' => 'Times-Bold',
'timesI' => 'Times-Italic',
'timesBI' => 'Times-BoldItalic',
'symbol' => 'Symbol',
'zapfdingbats' => 'ZapfDingbats');

Do góry

Metoda

Ta metoda zwróci nam obiekt klasy "PDF", którego możemy użyć do zbudowania naszego dokumentu. Ustawia ona podstawowe parametry dokumentu takie jak orientacja i rozmiar strony. Zwraca nasz obiekt.

function &factory($orientation = 'P', $format = 'A4')
{
   /* Stwórz obiekt klasy "PDF". */
   $pdf = &new PDF();
 
   /* Format strony. */
      $format = strtolower($format);
   if ($format == 'a3') { // Strona A3.
      $format = array(841.89, 1190.55);
   } elseif ($format == 'a4') { // Strona A4.
      $format = array(595.28, 841.89);
   } elseif ($format == 'a5') { // Strona A5.
      $format = array(420.94, 595.28);
   } elseif ($format == 'letter') { // Papier listowny.
      $format = array(612, 792);
   } elseif ($format == 'legal') { // Format "Legal" (???).
      $format = array(612, 1008);
   } else {
      die(sprintf('Nieznany format strony: %s', $format));
   }
   $pdf->_w = $format[0];
   $pdf->_h = $format[1];
 
   /* orientacja strony. */
   $orientation = strtolower($orientation);
   if ($orientation == 'l' || $orientation == 'landscape') {
      $w = $pdf->_w;
      $pdf->_w = $pdf->_h;
      $pdf->_h = $w;
   } elseif ($orientation != 'p' && $orientation != 'portrait') {
      die(sprintf('Nieprawidłowa orientacja: %s', $orientation));
   }
 
   /* Włącz domyślnie kompresję. */
   $pdf->setCompression(true);
 
   return $pdf;
}

Włączamy tutaj także domyślnie kompresowanie naszego dokumentu. Spowoduje to, że wygenerowany dokument PDF będzie o wiele mniejszy. Aktualna metoda "setCompression()" wygląda tak:

function setCompression($compress)
{
   /* Jeśli nie ma modułu do kompresji GZ (zlib), wyłącz kompresję */
   $this->_compress = (function_exists('gzcompress') ? $compress : false);
}

Na razie będziesz mógł wyłączyć kompresję, co pozwoli otworzyć Ci dokument PDF w zwykłym edytorze tekstu i szybko zobaczyć, co się tam dzieje.

Do góry

Wprowadzanie zawartości

Nie będziemy wprowadzać danych bezpośrednio do pliku PDF. Całą zawartość przetrzymamy w buforze i dopiero podczas zamykania dokumentu będziesz decydować, co z nią należy zrobić (np. zapisać do pliku, czy przesłać do przeglądarki). W takim razie musimy napisać coś, co pozwoli nam buforować dane. Będzie to prywatna metoda, niewykorzystywana poza klasą PDF.

function _out($s)
{
   if ($this->_state == 2) {
      $this->_pages[$this->_page] .= $s . "\n";
   } else {
      $this->_buffer .= $s . "\n";
   }
}

Zapewne zauważyłeś, że w prezentowanym kodzie użyłem paru pól klasy. Przyjrzyjmy im się dokładniej. Pole $this->_state przechowuje numer jednego z czterech stanów, w jakich może być nasz dokument PDF:

0 = zainicjowany
1 = dokument otwarty, strona nie
2 = otwarta strona
3 = dokument zamknięty

Stan jest bardzo ważny, gdyż pozwala nam określić, jak należy zbuforować dane wyjściowe. Jeśli mamy otwartą jakąś stronę, są one przesyłane do tablicy $this->_pages. W jakimkolwiek innym przypadku wszystko ląduje w buforze głównym - $this->_buffer. Podział taki jest konieczny, gdyż treść strony jest przechowywana jako osobny obiekt w dokumencie, co będzie wymagało później trochę dodatkowej pracy przy właściwym generowaniu wyjścia.

Jak zobaczysz w następnych przykładach, pole $this->_state jest używane także w innych miejscach do określania bieżącego stanu dokumentu.

Zaleca używać się znaku nowej linii ("\n") po wprowadzeniu każdej porcji danych wyjściowych, gdyż czasami jest to po prostu konieczne (na przykład główne instrukcje PDF muszą zaczynać się w nowej linii). Pamiętaj także, że w PDF'ie ważna jest wielkość liter. Sprawdzaj zatem, czy wszystko jest dokładnie takie, jak przewiduje to składnia języka.

Do góry

Otwarcie nowego dokumentu

Te dwie linijki są konieczne, by poprawnie utworzyć nowy dokument. Muszą być one wywołane przed wykonaniem jakiejkolwiek innej operacji.

function open()
{
   $this->_state = 1;         // Ustaw stan na zainicjowany
   $this->_out('%PDF-1.3');   // Wyślij nagłówek PDF
}

Druga linijka wysyła specjalny nagłówek pozwalający zidentyfikować typ pliku, oraz używaną wersję formatu PDF. Numery wersji pozwalają przeglądarkom PDF'ów odpowiednio interpretować zawarte w plikach instrukcje. Artykuł ten nie będzie demonstrował żadnych egzotycznych rzeczy, a więc wystarczy nam wersja 1.3. Jeśli jednak chcesz używać bardziej zaawansowanych możliwości formatu w wersji 1.5, musisz zmienić numer wersji.

Do góry

Dodawanie strony

Teraz możemy dodać jakąś stronę do naszego dokumentu. Kod jest w miarę prosty.

Jedyną rzeczą wymagającą trochę większej uwagi, jest sprawdzenie pola $this->_font_family. Dla każdego tekstu wprowadzanego do dokumentu musimy ustawić jakąś czcionkę. Jednakże, musimy wziąć pod uwagę taką ewnetualność, że została ona ustawiona, zanim dodaliśmy jakąkolwiek stronę, bądź ustawiliśmy ją dla strony poprzedniej. Należy zatem sprawdzić typ obecnej czcionki i wpisać go do nowootwartej strony. Wykorzystujemy do tego metodę setFont(), którą napiszemy później.

function addPage()
{
   $this->_page++; // Zwiększ licznik stron
   $this->_pages[$this->_page] = ''; // Zainicjuj bufor strony
   $this->_state = 2; // Poinformuj skrypt, że strona jest otwarta
 
   /* Sprawdź, czy czcionka została ustawiona wcześniej */
   if ($this->_font_family) {
      $this->setFont($this->_font_family, $this->_font_style, $this->_font_size);
   }
}

Do góry

Wprowadzanie prostego tekstu

Jak wspomniałem wcześniej, przed wyświetleniem jakiegokolwiek tekstu, musimy wprowadzić informację o użytej czcionce. Potrzebna nam jest zatem metoda, która to zrobi. Specyfikacja PDF przewiduje zbiór predefiniowanych czcionek, które mogą być użyte bez żadnych dodatkowych informacji dla przeglądarki. Możesz także dołączyć własne czcionki do dokumentu, lecz wykracza to poza zakres tego artykułu. Na razie ogranicz listę używanych czcionek do:

Poniższa metoda ustawia odpowiednią czcionkę, a także (opcjonalnie) jej styl (pogrubiony, kursywa itp.), oraz rozmiar.

function setFont($family, $style = '', $size = null)
{
   $family = strtolower($family);
   if ($family == 'arial') {            // Użyj helvetica.
      $family = 'helvetica';
   } elseif ($family == 'symbol' ||      // Brak styli dla tych dwóch czcionek
      $family == 'zapfdingbats') {
      $style = '';
   }
 
   $style = strtoupper($style);
   if ($style == 'IB') {      // akceptuj dowolna kolejnosc parametrow styli
      $style = 'BI';
   }
 
   if (is_null($size)) {      // Brak wyszczegolnionego rozmiaru
      $size = $this->_font_size;   // wiec uzyj domyslnego
   }
 
   if ($this->_font_family == $family &&   // Jesli probujesz ustawic ta czcionke, co jest...
      $this->_font_style == $style &&
      $this->_font_size == $size) {      // zakoncz dzialanie funkcji
      return;
   }
 
   /* Ustaw klucz czcionki. */
   $fontkey = $family . $style;
 
   if (!isset($this->_fonts[$fontkey])) { // Sprawdz, czy zbuforowany
      $i = count($this->_fonts) + 1;     // Zwieksz licznik obiektow czcionek
      $this->_fonts[$fontkey] = array(
            'i' => $i,         // zapamietaj obecne ustawienie
            'name' => $this->_core_fonts[$fontkey]);
   }
 
   /* Ustaw obecne parametry . */
   $this->_font_family = $family;
   $this->_font_style = $style;
   $this->_font_size = $size;
   $this->_current_font = $this->_fonts[$fontkey];
 
   /* Wyswietl informacje o czcionce, jesli zdefiniowano jakas strone */
   if ($this->_page > 0) {
      $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->_current_font['i'], $this->_font_size));
   }
}

Następna metoda ułatwi zmianę rozmiarów czcionek, bez konieczności zaprzęgania do pracy całej metody setFont().

function setFontSize($size)
{
   if ($this->_font_size == $size) { // Jesli juz jest obecny rozmiar
      return;         // zostaw go w spokoju
   }
 
   $this->_font_size = $size;    // Ustaw czcionke
 
   /* Wyswietl informacje o czcionce, jesli zdefiniowano jakas strone */
   if ($this->_page > 0) {
      $this->_out(sprintf('BT /F%d %.2f Tf ET',
            $this->_current_font['i'],
            $this->_font_size));
   }
}

A teraz właściwa metoda do drukowania tekstu. Musisz podać jako parametry zarówno twój tekst, jak i jego pozycję x/y.

function text($x, $y, $text)
{
   $text = $this->_escape($text); // Przekonwertuj znaki specjalne
   $out = sprintf('BT %.2f %.2f Td (%s) Tj ET',
            $x, $this->_h - $y, $text);
   $this->_out($out);
}

Zauważ, jak sprytnie dajemy programiście możliwość stosowania współrzędnej Y mierzonej od góry strony (podczas gdy format PDF liczy ją od dołu). Aby tego dokonać, odejmujemy ją od wysokości strony ($this->_h - $y). Zwróć także uwagę na fakt, iż wprowadzony tekst jest "uwalniany" od znaków specjalnych tak, by mógł być bezpiecznie zapisany. Ponieważ jest on otoczony przez nawiasy, te muszą zostać w jakiś sposób zneutralizowane. Najlepszym wyjściem jest stworzenie oddzielnej metody unieszkodliwiającej wszystkie niebezpieczne znaki w tekście. Będziemy się do niej odwoływać jeszcze parę razy w tym artykule, lecz myślę, że i tobie się przyda, gdy zechcesz dodać do tej klasy nieco więcej funkcjonalności.

function _escape($s)
{
   $s = str_replace('\\', '\\\\', $s); // Konwertuj każdy '\\'
   $s = str_replace('(', '\\(', $s); // Konwertuj każdy '('
   return str_replace(')', '\\)', $s); // Konwertuj każdy ')'
}

Do góry

Zamykanie dokumentu

Zamknięcie dokumentu jest trochę bardziej złożone: musimy poskładać wszystko do kupy, dodać parę znaczników PDF'a, oraz stworzyć kilka referencji. Ten kod pozwoli nam na końcowe obrobienie całej zbuforowanej zawartości i wygenerowanie ostatecznej wersji dokumentu PDF. Zaczniemy od sprawdzenia, czy w ogóle została dodana jakaś strona, oraz od ustawienia stanu na "dokument zamknięty"...

function close()
{
   if ($this->_page == 0) {   // Jeśli mamy niezainicjowany dokument, dodamy
      $this->addPage();      // jedną stronę, by zrobić z tego poprawny plik PDF.
   }
 
   $this->_state = 1; // Ustaw odpowiedni stan. 

Teraz należy wstawić wszystkie strony i zasoby, które buforowaliśmy oddzielnie. Do każdej z przedstawionych tu metod wrócimy później.

   /* Strony i zasoby. */
   $this->_putPages();
   $this->_putResources();

Następnie wstawimy trochę informacji o dokumencie. PDF przechowuje je jako oddzielny obiekt i tu z pomocą przyjdzie nam metoda _newobj(). Możesz dodać do tej części inne informacje typu autor, temat, tytuł, słowa kluczowe. Na razie zamieścimy tu tylko informację o generatorze pliku.

   /* Print some document info. */
   $this->_newobj();
   $this->_out('<<');
   $this->_out('/Producer (Moja pierwsza klasa PDF)');
   $this->_out(sprintf('/CreationDate (D:%s)',
            date('YmdHis')));
   $this->_out('>>');
   $this->_out('endobj');

Następna sekcja to katalog PDF, który decyduje o tym, jak dokument będzie wyglądał w przeglądarce. Możesz zostawić to w takim stanie, jak zaprezentowałem, gdyż nie dzieje się tu nic ciekawego (aczkolwiek jest to niezbędne).

   /* Wyślij katalog */
   $this->_newobj();
   $this->_out('<<');
   $this->_out('/Type /Catalog');
   $this->_out('/Pages 1 0 R');
   $this->_out('/OpenAction [3 0 R /FitH null]');
   $this->_out('/PageLayout /OneColumn');
   $this->_out('>>');
   $this->_out('endobj');

Sekcja referencji jest niezwykle ważna. Wykorzystuje ona tablicę $this->_offset, którą przedstawiłem wcześniej. PDF składuje binarny adres do wszystkich obiektów w dokumencie. Pozwala do przeglądarce na odczytywanie ich w dowolnej kolejności bez potrzeby przeglądania na nowo całego dokumentu.

   /* Print cross reference. */
   $start_xref = strlen($this->_buffer); // Pobierz adres xref
   $this->_out('xref');          // Wyślij go
   $this->_out('0 ' . ($this->_n + 1));  // Ilość obiektów...
   $this->_out('0000000000 65535 f ');
   /* Przejrzyj wszystkie obiekty i wyświetl ich adresy */
   for ($i = 1; $i <= $this->_n; $i++) {
      $this->_out(sprintf('%010d 00000 n ', $this->_offsets[$i]));
   }

Każdy obiekt jest drukowany w osobnej linii. Jego adres jest 10-cyfrową liczbą, poprzedzoną numerem generacji, oraz będącyh aktualnie w użyciu wskaźnikiem. Na razie nie musisz się o nie martwić, są one używane tylko podczas edycji dokumentów PDF, oraz kasowania obiektów. Jako że my je tworzymy, numer generacji zawsze będzie ustawiony na 00000, a wskaźnik na 'n'.

Na końcu musimy wygenerować nagłówek PDF.

   /* Wyświetl nagłówek */
   $this->_out('trailer');
   $this->_out('<<');
   /* Łączna liczba obiektów */
   $this->_out('/Size ' . ($this->_n + 1));
   /* Obiekt główny */
   $this->_out('/Root ' . $this->_n . ' 0 R');
   /* Obiekt informacji o dokumencie */
   $this->_out('/Info ' . ($this->_n - 1) . ' 0 R');
   $this->_out('>>');
   $this->_out('startxref');
   $this->_out($start_xref); // Gdzie znajdziemy xref'a
   $this->_out('%%EOF');
   $this->_state = 3; // Ustaw stan dokumentu na zamknięty
}

Przyjrzyjmy się teraz nowym metodom poznanym podczas zamykania dokumentu. _newobj() jest używana do dodawania i zapamiętywania wszystkich nowych obiektów dodawanych do pliku.

function _newobj()
{
   /* Zwiększ licznik obiektów */
   $this->_n++;
   /* Zapisz adres obiektu */
   $this->_offsets[$this->_n] = strlen($this->_buffer);
   /* Dodaj go do bufora */
   $this->_out($this->_n . ' 0 obj');
}

Metoda _putPages() przesyła na wyjście zawartość stron z tablicy $this->_pages, która przechowywała treść oddzielnie. Jeśli włączyłeś kompresję, cały tekst zostanie przepuszczony przez funkcję gzcompress() przed zapisaniem. Tu możesz także zobaczyć, czemu licznik obiektów zaczyna się od 2. Ustawiamy główne strony jako obiekt numer 1, a później dostrzeżesz, że wszystkie zasoby zapisujemy jako obiekt numer 2. Dzięki temu łatwiej nam je zaadresować, gdy tego potrzebujemy (np. w każdym obiekcie strony).

function _putPages()
{
   /* Jeśli mamy włączoną kompresję, ustaw odpowiedni znacznik */
   $filter = ($this->_compress) ? '/Filter /FlateDecode ' : '';
   /* Wyświetl strony */
   for ($n = 1; $n <= $this->_page; $n++) {
      $this->_newobj();      // Rozpocznij nowy obiekt
      $this->_out('<</Type /Page');  // Typ obiektu
      $this->_out('/Parent 1 0 R');
      $this->_out('/Resources 2 0 R');
      $this->_out('/Contents ' . ($this->_n + 1) . ' 0 R>>');
      $this->_out('endobj');
 
      /* Jeśli mamy kompresować, przepuśćmy tekst przez funkcje gzcompress() */
      $p = ($this->_compress) ? gzcompress($this->_pages[$n]) : $this->_pages[$n];
 
      /* Wyświetlamy zawartość strony */
      $this->_newobj();      // Rozpocznij nowy obiekt
      $this->_out('<<' . $filter . '/Length ' . strlen($p) . '>>');
      $this->_putStream($p);// Wyślij zawartość
      $this->_out('endobj');
   }
 
   /* Ustaw adres pierwszego obiektu */
   $this->_offsets[1] = strlen($this->_buffer);
   $this->_out('1 0 obj');
   $this->_out('<</Type /Pages');
   $kids = '/Kids [';
   for ($i = 0; $i < $this->_page; $i++) {
      $kids .= (3 + 2 * $i) . ' 0 R ';
   }
   $this->_out($kids . ']');
   $this->_out('/Count ' . $this->_page);
   /* Output the page size. */
   $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',
         $this->_w, $this->_h));
   $this->_out('>>');
   $this->_out('endobj');
}

Zapoznajmy się teraz z kolejną metodą: _putStream(). Mogliśmy cały ten kod dołączyć do obecnej metody _putPages(), jednakże z powodu wykorzystywania go przy dodawaniu np. obrazków, musimy go oddzielić.

function _putStream($s)
{
   $this->_out('stream');
   $this->_out($s);
   $this->_out('endstream');
}

Zasoby są rozmaitymi dodatkami do naszego dokumentu, takimi jak obrazki, czy czcionki. Poniższa metoda ma za zadanie dołączyć je w odpowiednim miejscu.

function _putResources()
{
   /* Wyświetl czcionki */
   $this->_putFonts();
 
   /* Zasoby są zawsze obiektem numer 2 */
   $this->_offsets[2] = strlen($this->_buffer);
   $this->_out('2 0 obj');
   $this->_out('<</ProcSet [/PDF /Text]');
   $this->_out('/Font <<');
   foreach ($this->_fonts as $font) {
      $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
   }
   $this->_out('>>');
   $this->_out('>>');
   $this->_out('endobj');
}

Ta ostatnia metoda prywatna, zaprezentowana w powyższym przykładzie, dołącza informacje o wszystkich czcionkach do pliku PDF. Jako że na razie możemy używać tylko czcionek domyślnych, nie robi ona nic więcej, jak tylko wypisuje użyte typy.

function _putFonts()
{
   /* Wyświetl dane o czcionkach */
   foreach ($this->_fonts as $k => $font) {
      $this->_newobj();
      $this->_fonts[$k]['n'] = $this->_n;
      $name = $font['name'];
      $this->_out('<</Type /Font');
      $this->_out('/BaseFont /' . $name);
      $this->_out('/Subtype /Type1');
      if ($name != 'Symbol' && $name != 'ZapfDingbats') {
         $this->_out('/Encoding /WinAnsiEncoding');
      }
      $this->_out('>>');
      $this->_out('endobj');
   }
}

Do góry

Wyświetlanie dokumentu

Ostatnia metoda nie robi nic więcej, jak upewnia się, czy dokument jest zamknięty, wysyła kilka nagłówków, a następnie zbuforowane dane.

function output($filename)
{
   if ($this->_state < 3) {   // Jeśli dokument nie został zamknięty, robimy to teraz
      $this->close();
   }
 
   /* Upewniamy się, że jeszcze nic nie zostało wysłane */
   if (headers_sent()) {
      die('Nie można wysłać pliku PDF do przeglądarki, gdyż wcześniej zostały do niej wysłane jakieś inne dane!');
   }
 
   /* Zaoferuj ściągnięcie pliku */
   $agent = trim($_SERVER['HTTP_USER_AGENT']);
   if ((preg_match('|MSIE ([0-9.]+)|', $agent, $version)) ||
      (preg_match('|Internet Explorer/([0-9.]+)|', $agent, $version))) {
      header('Content-Type: application/x-msdownload');
      Header('Content-Length: ' . strlen($this->_buffer));
      if ($version == '5.5') {
         header('Content-Disposition: filename="' . $filename . '"');
      } else {
         header('Content-Disposition: attachment; filename="' . $filename . '"');
      }
   } else {
      Header('Content-Type: application/pdf');
      Header('Content-Length: ' . strlen($this->_buffer));
      Header('Content-disposition: attachment; filename=' . $filename);
   }
   echo $this->_buffer;
}

Do góry

Przykładowe użycie
<?php
require 'PDF.php';         // Dołącz klasę
$pdf = &PDF::factory('p', 'a4');   // Stwórz obiekt klasy "PDF"
$pdf->open();            // Utwórz nowy dokument
$pdf->setCompression(true);      // Aktywuj kompresję
$pdf->addPage();         // Utwórz stronę
$pdf->setFont('Courier', '', 8);   // Ustaw czcionkę Arial o rozmiarze 8
$pdf->text(100, 100, 'First page');   // Dodaj tekst na pozycji x=100 i y=100
$pdf->setFontSize(20);         // Zmień rozmiar czcionki na 20
$pdf->text(100, 200, 'HELLO WORLD!');   // Dodaj tekst na pozycji x=100 i y=200
$pdf->addPage();         // Dodaj nową stronę
$pdf->setFont('Arial', 'BI', 12);   // Ustaw pogrubioną i pochyloną czcionkę Arial o rozmiarze 12
$pdf->text(100, 100, 'Second page');      // Dodaj tekst na pozycji x=100 i y=200.
$pdf->output('foo.pdf');      // Wstaw wygenerowany dokument do pliku foo.pdf
?>

Do góry

Kompletna klasa

Możesz ściągnąć pełen kod klasy spod tego adresu.

Do góry

O autorze

Marko Djukic mieszka i pracuje we Florencji (Włochy) w swej własnej firmie http://oblo.com, której głównym zadaniem jest dostarczanie innowacyjnych rozwiązań Open Source dla lokalnych władz, oraz przedsiębiorstw. Jest także jednym z głównych projektantów projektu Horde (http://horde.org). Możesz się z nim skontaktować poprzez e-mail marko@oblo.com.

--
Przetłumaczone dla serwisu Webcity.pl przez Zyx'a za zgodą autora.

Do góry

Waszym zdaniem:

Nikt jeszcze nie dodał swojego komentarza. Możesz być pierwszy!


Twoim zdaniem:

Reklama

banner

Partnerzy

CityDesign.pl
phpSolutions