Jumat, 07 Oktober 2011

Membuat robot web crawler dengan PHP

Membuat robot web crawler dengan PHP



Beberapa waktu yang lalu ada sebuah posting yang membahas tentang sebuah situs yang bisa mengambil isi situs lain dan kemudian disimpan dalam format PDF sehingga bisa dibaca secara offline. Nah, sekarang kita juga akan membuat program semacam situs itu dimana nanti program PHP yang kita buat akan mampu mengambil isi situs lain, mengubahnya menjadi format PDF dan kemudian siap didownload untuk disimpan. Kita akan menggunakan cURL (baca: see URL) yang ada di PHP untuk mengambil content situs orang lain, dan untuk mengubah content tersebut menjadi format PDF kita akan menggunakan class TCPDF. Saya juga akan menggunakan class buatan saya sendiri yang saya beri nama JSLoc untuk "memvalidasi" isi situs sebelum dimasukkan pada method di class TCPDF.



1. Pengenalan



- Apakah cURL?

    Menurut PHP manual, cURL (baca (*sekali lagi): see URL) adalah sebuah pustaka (library) yang dibuat oleh Daniel Stenberg dimana kita bisa melakukan koneksi dan komunikasi pada banyak jenis server dengan banyak jenis protokol. Pustaka ini mendukung protokol http, https, ftp, gopher, telnet, dict, file, dan ldap (*buat yang bingung segera cari sesuatu untuk dipegang, hehehe..). Selain itu dia juga mendukung sertifikat HTTPS, HTTP POST, HTTP PUT, unggah dengan FTP, unggah dengan form HTTP, proxies, cookies dan autentifikasi user+password. Untuk menggunakan cURL kamu tidak perlu menginstal apa-apa, hampir semua server (ataupun yang pakai xampp, dsb) biasanya menggunakan PHP yang sudah memiliki library cURL.



- Apakah TCPDF?

    TCPDF adalah sebuah class di PHP yang dapat digunakan untuk membuat file PDF secara langsung tanpa membutuhkan ekstensi eksternal. Dia mendukung semua format halaman, character set, bahasa right-to-left, bermacam fonts, memproses image, dan banyak lagi. Kamu bisa dapatkan versi terbaru dari TCPDF di www.tcpdf.org atau www.sourceforge.net/projects/tcpdf



- Apakah JSLoc?

    JSLoc adalah sebuah class (*buatan saya sendiri, dan sekalian promosi, hehehe..) yang digunakan untuk melakukan optimasi dan validasi kode HTML, CSS dan Javascript. Kita akan membuang tag-tag HTML yang tidak diperlukan menggunakan class ini. Kamu bisa dapatkan versi terbaru (*sementara cuma ada satu versi :D) di www.sourceforge.net/projects/jsloc



- Gantengan mana hedy sama anaknya?

    Nah, yang ini gak nyambung dan gak perlu dibahas...



2. Persiapan



Mungkin pertama kamu bisa mencoba program yang akan kita buat di mesin lokal dulu, jadi letakkan file PHP yang akan kita buat nanti di foler "htdocs" (jika menggunakan Apache). Unduh dan kemudian ekstrak TCPDF dan JSLoc sesuai dengan gambaran path file berikut:





Untuk TCPDF sebaiknya letakkan pada folder khusus seperti pada gambaran path diatas, trus file dengan nama "fileyangakankitabuat.php" atau kalau disingkat "fyakb.php" bisa kamu ubah menjadi "index.php" atau nama apapun terserah kamu asalkan tetap menggunakan ekstension php. Sedangkan file "web2pdf.pdf" jangan diganti namanya kecuali kamu tahu alasannya.



3. Mulai coding



Oke, daripada berlama-lama entar malah jadi bosen, kita langsung aja memulai pembuatan program ini. Saya akan membuat sebuah halaman antarmuka dan sebuah file berisi class lengkap yang mungkin jadi agak panjang kodenya, karena itu siapkan minuman penyegar mata (*emang ada?) biar bisa terus jelalatan memandang kode berikut. Saya akan gunakan cara lama yaitu semua kode saya lempar semua baru dijelaskan. Ini kode lengkapnya: (*oh iya, jika kamu udah eneg duluan, kamu bisa unduh source code dari program ini di: http://bit.ly/n0iEhK  , atau mencoba program yang sudah jadi dan sudah online di: http://bit.ly/mTwonU ).



Kita akan membuat interfacenya dulu, dimana halaman ini cuma berisi form yang berisi inputan untuk memasukkan URL dari situs yang akan diambil contentnya. Setelah form dikirim maka file ini juga sekaligus menjadi responder yang akan memanggil class "web2pdf" yang akan kita simpan di file "web2pdf.php". Isi dari file "fyakb.php" adalah seperti berikut:



<?php

if (isset($_POST['url']) && ! empty($_POST['url'])) {

    if ( ! file_exists('tcpdf/tcpdf.php')) {

        return trigger_error('Class TCPDF tidak ditemukan, unduh dulu!', E_USER_ERROR);

    } else {

        require_once('tcpdf/tcpdf.php');

    }



    @require_once('web2pdf.php');



    $w2pdf = new Web2PDF($_POST['url']);

    $w2pdf->with_image(TRUE);

    $w2pdf->get_result();

}

?><html>

<head>

<title>Web 2 PDF</title>

<style type="text/css">

body {

    font-family: "Lucida Grande", Tahoma, Verdana, Arial;

    font-size: 0.8em;

}

#main { width: 80%; margin: 20px 10%; }

</style>

</head>

<body>

    <div id="main">

        <h1>Web To PDF</h1><br />

        <p>

            Web2PDF akan membuat salinan content dari suatu website ke dalam format PDF. Anda masukkan saja URL dari situs target (terserah, tapi biasanya yang berisi artikel atau berita) lalu klik SIMPAN. Jika berhasil maka Anda akan mulai mengunduh dokumen PDF yang berisi dokumen dari URL yang Anda masukkan. Semoga berhasil :D

        </p>

        <br />

        <form method="post" action="">

            <label for="url">Masukkan URL: </label>

            <input type="text" name="url" maxlength="120" style="width: 400px; height: 28px; border: 1px solid #C5C5EE;" />

            <input type="submit" name="submit" value="SIMPAN" />

        </form>

    </div>

</body>

</html>



Dibaris-baris awal kamu akan menemukan beberapa baris kode PHP yang berhubungan dengan respon bila user mengklik tombol SIMPAN. Disitu kita memasukkan beberapa file dengan perintah require_once, dan setelah itu kita membuat sebuah object dengan nama "$w2pdf" yang berasal dari class "Web2PDF". Pada constructor class ini kita bisa mengeset URL yang menjadi target karena itu kita menggunakan perintah "$w2pdf = new Web2PDF($_POST['url']);" yang mana kita melewatkan isi dari $_POST['url'] ke fungsi constructornya.



Setelah itu kita tentukan apakah file PDF nanti juga memasukkan image/gambar dengan menggunakan method "with_image" dan diberi nilai TRUE (berarti dengan gambar, defaultnya FALSE berarti tanpa gambar). Pemberian gambar pada dokumen mungkin saja menyebabkan kegagalan karena banyak faktor, jadi misalkan kamu mencoba menggunakan gambar selalu gagal maka ada baiknya kamu tidak menggunakan gambar dengan cara: "$w2pdf->with_image(FALSE);" atau dengan tidak memanggil method ini sama sekali karena nilai defaultnya adalah FALSE.



Kemudian untuk menampilkan output kita menggunakan method "get_result" seperti ini: "$w2pdf->get_result();" yang sebenarnya method ini memiliki satu parameter yang menentukan apakah output PDF akan langsung diunduh (defaultnya) atau disimpan di server. Untuk menyimpan di server di sebelah file "fyakb.php" kita cukup menggunakan parameter bernilai TRUE pada pemanggilan method ini, misalnya: "$w2pdf->get_result(TRUE);"



Sekarang kita akan membuat class Web2PDF yang nantinya akan disimpan di file "web2pdf.php" yang diletakkan disebelah file "fyakb.php". Isi filenya seperti ini (kodenya memang agak panjang tapi gak pendek, hehehe):



<?php

/**

 * Web crawler yang menyimpan content website menjadi PDF

 * @author    M. Nazir Arifin

 * @date    2011, October 1

 */

class Web2PDF {

    private $url       = '',

            $wimage    = FALSE,

            $doc_cont  = '',

            $doc_title = '';



    public static $purl = '';



    public function __construct($url = '') {

        if ( ! function_exists('curl_init'))

            return trigger_error('Library cURL tidak tersedia', E_USER_ERROR);



        if ( ! class_exists('jsloc')) {

            if ( ! file_exists('jsloc.php'))

                return trigger_error('Class JSLoc tidak ditemukan, unduh dulu!', E_USER_ERROR);

            else

                @include_once('jsloc.php');

        }



        if ( ! empty($url)) $this->set_url($url);

    }



    public function set_url($url = '') {

        if (empty($url))

            return trigger_error('URL kosong dicoba diset!', E_USER_ERROR);



        if ( ! preg_match('/^(http|https|ftp)/', $url))

            $url = 'http://' . $url;



        if ( ! filter_var($url, FILTER_VALIDATE_URL))

            return trigger_error('URL tidak valid!', E_USER_ERROR);



        self::$purl = $this->url = $url;

    }



    public function with_image($p = FALSE) {

        if ($p != $this->wimage && is_bool($p)) $this->wimage = $p;

    }



    public function get_result($save_pdf = FALSE) {

        if ( ! empty($this->url)) {

            $this->do_curl();

            $this->filter_html();

            $this->gen_pdf($save_pdf);

        } else {

            return trigger_error('URL belum ditentukan!', E_USER_ERROR);

        }

    }



    private function do_curl() {

        $ch = curl_init();



        curl_setopt_array($ch, array(

            CURLOPT_URL => $this->url,

            CURLOPT_AUTOREFERER => TRUE,

            CURLOPT_FAILONERROR => TRUE,

            CURLOPT_USERAGENT => 'web2pdf 1.0',

        ));

        $redirect       = 0;

        $this->doc_cont = $this->curl_redirect_exec($ch, $redirect);

        curl_close($ch);



        if ($this->doc_cont === FALSE)

            return trigger_error('cURL gagal total :(, <a href="">Kembali?</a>', E_USER_ERROR);

    }



    private function curl_redirect_exec($ch, &$redirects, $curlopt_header = FALSE) {

        curl_setopt($ch, CURLOPT_HEADER, TRUE);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);



        $data = curl_exec($ch);

        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);



        if ($http_code == 301 || $http_code == 302) {

            list ($header) = explode("\r\n\r\n", $data, 2);

            $matches = array();

            preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);

            $url = trim(array_pop($matches));

            $url_parsed = parse_url($url);



            if ( ! empty($url_parsed)) {

                self::$purl = $this->url = $this->fix_url($url);

                curl_setopt($ch, CURLOPT_URL, $this->url);

                $redirects++;

                return $this->curl_redirect_exec($ch, $redirects);

            }

        }



        if ($curlopt_header)

            return $data;

        else {

            list(,$body) = explode("\r\n\r\n", $data, 2);

            return $body;

        }

    }



    private function filter_html() {

        if (empty($this->doc_cont)) return;



        $allow_tags = array(

            'a', 'b', 'blockquote', 'br', 'dd', 'del', 'div', 'dl', 'dt', 'em', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'li', 'ol', 'p', 'pre', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tcpdf', 'td', 'th', 'thead', 'tr', 'tt', 'u', 'ul',

        );



        if ($this->wimage === TRUE) $allow_tags[] = 'img';

        jsloc::allowable_tags($allow_tags);

        $this->doc_cont = jsloc::minHTML($this->doc_cont);



        $this->doc_cont = preg_replace('/^(.*)(<body>)(.+)(<\/body>)(<\/html>)$/', '$2$3$4', $this->doc_cont);

        $doc = new DOMDocument('1.0', 'UTF-8');

        @$doc->loadHTML($this->doc_cont);



        $title = @$doc->getElementsByTagName('title')->item(0)->nodeValue;

        $this->doc_title = (is_null($title) ? $this->url : $title);



        $links = @$doc->getElementsByTagName('a');

        for ($i = 0; $i < $links->length; $i++) {

            $att = $links->item($i)->attributes;

            foreach ($att as $item) {

                if ($item->name == 'href') {

                    $item->value = htmlentities($item->value);

                }

            }

        }



        if ($this->wimage === TRUE) {

            $imgs = @$doc->getElementsByTagName('img');

            for ($i = 0; $i < $imgs->length; $i++) {

                $att     = $imgs->item($i)->attributes;

                foreach ($att as $item) {

                    if ($item->name == 'src')

                        $item->value = htmlentities($item->value);

                }

            }

        }



        $this->doc_cont = $doc->saveHTML();

        $this->doc_cont = preg_replace_callback('/(src|href)=(")*([^"\'>]+)(")*/', create_function('$str', '

            $w2pdf = new Web2PDF(Web2PDF::$purl);

            return $str[1] . \'="\' . $w2pdf->fix_url(html_entity_decode($str[3])) . \'"\';

        '), $this->doc_cont);

    }



    private function gen_pdf($save_pdf = FALSE) {

        require_once('tcpdf/config/lang/eng.php');

        $fln = preg_replace('/[^a-z0-9]/', '', $this->doc_title) . '.pdf';



        $pdf = new MyPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);



        $pdf->SetCreator('Web2PDF');

        $pdf->SetAuthor('M. Nazir Arifin');

        $pdf->SetTitle($this->doc_title);

        $pdf->SetSubject('Web To PDF');

        $pdf->SetKeywords('nazir, nazir lagi, nazir cool, nazir the best, narsis on here, oke, ehm..., enough, stop!');

        $pdf->SetFooterText($this->url);

        $pdf->SetHeaderData('', 0, 'Web2PDF', "by M. Nazir Arifin\n\n" . $this->doc_title);

        $pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));

        $pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));

        $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);

        $pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);

        $pdf->SetHeaderMargin(PDF_MARGIN_HEADER);

        $pdf->SetFooterMargin(PDF_MARGIN_FOOTER);

        $pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);

        $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);

        $pdf->setLanguageArray($l);

        $pdf->SetFont('helvetica', '', 9);

        $pdf->AddPage();

        $pdf->writeHTML($this->doc_cont, true, 0, true, 0);

        $pdf->lastPage();

        $pdf->Output($fln, $save_pdf === FALSE ? 'D' : 'F');

    }



    public function fix_url($url = '') {

        $purl = parse_url($this->url);

        $surl = parse_url(html_entity_decode($url));



        if ($url == '' OR $url == '#')

            return $this->url . $url;



        if ( ! isset($surl['scheme'])) {

            if ( ! isset($surl['host'])) {

                $split         = explode('/', $surl['path']);

                if (isset($purl['path']) && dirname($purl['path']) != '.')

                    array_unshift($split, ltrim(dirname($purl['path']), '/'));



                $query       = ( ! isset($surl['query'])) ? '' : '?' . $surl['query'];

                $frag        = ( ! isset($surl['fragment'])) ? '' : '#' . $surl['fragment'];



                if ($split[0] == '.') array_shift($split);

                while ($split[0] == '..')

                    @array_shift($split);



                return $purl['scheme'] . '://' . $purl['host'] . '/' . implode('/', $split) . $query . $frag;

            } else

                return $purl['scheme'] . '://' . $url;

        } else return $url;

    }

}



class MyPDF extends TCPDF {

    public function SetFooterText($t = '') {

        $this->footer_text = $t;

    }



    public function Footer() {

        $this->SetY(-15);

        $this->SetFont('helvetica', 'I', 8);

        $this->Cell(0, 10, "sumber asli: {$this->footer_text}", 0, 0, 'L');

    }

}



Haruskah aku menjelaskan perbarisnya? sepertinya tidak :). Oh iya, jika kamu mengunduh source program ini pada link diatas maka saya memberi beberapa komentar yang mungkin sedikit (sekali) membantu mengenai apa yang dikerjakan oleh kode tersebut. Saya coba sedikit saja memberikan gambaran kode diatas, apa saja yang dilakukan dan bagaimana prosesnya. Kita memiliki dua class yaitu "Web2PDF" yang menjadi class pemroses dan class "MyPDF" (turunan dari class TCPDF) yang terpaksa aku buat untuk membuat custom footer. Kita bahas class Web2PDF saja karena class MyPDF secara teknis tidak begitu berpengaruh pada alur program keseluruhan (tapi jangan dihapus lho ya..!).



Class Web2PDF memiliki tiga public method (get_result, set_url, with_image) dan empat private method (do_curl, curl_redirec_exec, filter_html, gen_pdf, fix_url). Semoga dari nama methodnya kamu bisa punya bayangan tentang apa yang dilakukan oleh tiap-tiap method. Dari ketiga public method yang saya sebutkan mungkin yang belum kenal adalah method set_url. Method ini sebenarnya dipanggil oleh constructor ketika kamu memasukkan sebuah nilai saat pembuatan object. Untuk mengeset URL secara manual tanpa menggunakan constructor maka kamu bisa menggunakan method set_url ini sebelum memanggil fungsi get_result().



Sekarang kita bahas private methodnya.

    1. do_curl(), berikut ini cuplikan kodenya:



        $ch = curl_init();



        curl_setopt_array($ch, array(

            CURLOPT_URL => $this->url,

            CURLOPT_AUTOREFERER => TRUE,

            CURLOPT_FAILONERROR => TRUE,

            CURLOPT_USERAGENT => 'web2pdf 1.0',

        ));

        $redirect       = 0;

        $this->doc_cont = $this->curl_redirect_exec($ch, $redirect);

        curl_close($ch);



        if ($this->doc_cont === FALSE)

            return trigger_error('cURL gagal total :(, <a href="">Kembali?</a>', E_USER_ERROR);



Pertama kita buat resource untuk curl menggunakan fungsi curl_init(). Setelah itu kita set beberapa konfigurasi pada resource yang kita buat menggunakan fungsi curl_setopt_array(). Parameter kedua dari fungsi ini adalah berisi array dengan menggunakan key index berupa konstanta yang sudah ada di PHP. Untuk arti lengkap dan daftar lengkap dari konstanta di curl kamu bisa melihat ke situs www.php.net/curl_setopt



Pada awalnya kodenya tidak seperti ini. Waktu aku tes di webserver online ternyata safe_mode dan open_basedir didisabled sehingga aku tidak bisa menggunakan konstanta CURLOPT_FOLLOWLOCATION. Setelah aku baca di php.net ada source yang bisa mengatasi permasalah ini sehingga curl kita nanti tetap akan mengikuti redirect dari situs target. Methodnya bernama curl_redirect_exec dimana nanti dalam method ini kita akan memanggil fungsi curl_exec dan jika ada redirect maka akan diulangi membuat koneksi curl lagi. Ini kodenya:



    2. curl_redirect_exec()



        curl_setopt($ch, CURLOPT_HEADER, TRUE);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);



        $data = curl_exec($ch);

        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);



        if ($http_code == 301 || $http_code == 302) {

            list ($header) = explode("\r\n\r\n", $data, 2);

            $matches = array();

            preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);

            $url = trim(array_pop($matches));

            $url_parsed = parse_url($url);



            if ( ! empty($url_parsed)) {

                self::$purl = $this->url = $this->fix_url($url);

                curl_setopt($ch, CURLOPT_URL, $this->url);

                $redirects++;

                return $this->curl_redirect_exec($ch, $redirects);

            }

        }



        if ($curlopt_header)

            return $data;

        else {

            list(,$body) = explode("\r\n\r\n", $data, 2);

            return $body;

        }



    Disitu kamu bisa melihat bahwa kita cek terlebih dahulu header yang dikirimkan oleh server. Jika kode header 301 (redirect permanen) atau 302 (redirect sementara) maka proses curl akan diulangi lagi dan menggunakan url baru sesuai dengan isi dari data Location/URI yang dalam header. Terakhir kita mereturn isi dari eksekusi curl yang telah dijalankan.



    2. filter_html(), berikut ini cuplikan kodenya:



        if (empty($this->doc_cont)) return;



        $allow_tags = array(

            'a', 'b', 'blockquote', 'br', 'dd', 'del', 'div', 'dl', 'dt', 'em', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'li', 'ol', 'p', 'pre', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tcpdf', 'td', 'th', 'thead', 'tr', 'tt', 'u', 'ul',

        );



        if ($this->wimage === TRUE) $allow_tags[] = 'img';

        jsloc::allowable_tags($allow_tags);

        $this->doc_cont = jsloc::minHTML($this->doc_cont);



        $this->doc_cont = preg_replace('/^(.*)(<body>)(.+)(<\/body>)(<\/html>)$/', '$2$3$4', $this->doc_cont);

        $doc = new DOMDocument('1.0', 'UTF-8');

        @$doc->loadHTML($this->doc_cont);



        $title = @$doc->getElementsByTagName('title')->item(0)->nodeValue;

        $this->doc_title = (is_null($title) ? $this->url : $title);



        $links = @$doc->getElementsByTagName('a');

        for ($i = 0; $i < $links->length; $i++) {

            $att = $links->item($i)->attributes;

            foreach ($att as $item) {

                if ($item->name == 'href') {

                    $item->value = htmlentities($item->value);

                }

            }

        }



        if ($this->wimage === TRUE) {

            $imgs = @$doc->getElementsByTagName('img');

            for ($i = 0; $i < $imgs->length; $i++) {

                $att     = $imgs->item($i)->attributes;

                foreach ($att as $item) {

                    if ($item->name == 'src')

                        $item->value = htmlentities($item->value);

                }

            }

        }



        $this->doc_cont = $doc->saveHTML();

        $this->doc_cont = preg_replace_callback('/(src|href)=(")*([^"\'>]+)(")*/', create_function('$str', '

            $w2pdf = new Web2PDF(Web2PDF::$purl);

            return $str[1] . \'="\' . $w2pdf->fix_url(html_entity_decode($str[3])) . \'"\';

        '), $this->doc_cont);



Ya, kita menggunakan jsloc dan DOM untuk memfilter dan menvalidasi kode HTML yang kita dapat dari cURL. Sekali lagi kode saya yang awal tidak seperti ini, dibaris-baris terakhir kamu akan melihat "keterpaksaan" saya membuat static properti (Web2PDF::$purl) demi memperbaiki url agar lebih valid. Seringkali alamat URL disitus orang menggunakan query yang berlebihan :) dan menggunakan tanda ampersand (&) sehingga DOM langsung macet karena ada entiti yang tidak diescape. Salah satu cara melakukan escape dengan menggunakan fungsi htmlentities pada URL tersebut, tapi tentu harus html_entity_decode biar kembali lagi. Nah, proses ini yang menambah panjang kode dan penambahan properti lain.



    3. gen_pdf(), dengan cuplikan kode seperti berikut:



        require_once('tcpdf/config/lang/eng.php');

        $fln = preg_replace('/[^a-z0-9]/', '', $this->doc_title) . '.pdf';



        $pdf = new MyPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);



        $pdf->SetCreator('Web2PDF');

        $pdf->SetAuthor('M. Nazir Arifin');

        $pdf->SetTitle($this->doc_title);

        $pdf->SetSubject('Web To PDF');

        $pdf->SetKeywords('nazir, nazir lagi, nazir cool, nazir the best, narsis on here, oke, ehm..., enough, stop!');

        $pdf->SetFooterText($this->url);

        $pdf->SetHeaderData('', 0, 'Web2PDF', "by M. Nazir Arifin\n\n" . $this->doc_title);

        $pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));

        $pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));

        $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);

        $pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);

        $pdf->SetHeaderMargin(PDF_MARGIN_HEADER);

        $pdf->SetFooterMargin(PDF_MARGIN_FOOTER);

        $pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);

        $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);

        $pdf->setLanguageArray($l);

        $pdf->SetFont('helvetica', '', 9);

        $pdf->AddPage();

        $pdf->writeHTML($this->doc_cont, true, 0, true, 0);

        $pdf->lastPage();

        $pdf->Output($fln, $save_pdf === FALSE ? 'D' : 'F');



Secara teknis, sebagian besar kode diatas ada di dokumentasi TCPDF, jadi kalau kamu ingin tahu lebih jauh tentang kode-kode diatas dan apa maksudnya bisa kamu lihat di situsnya TCPDF. Ada satu method yang saya buat dan digunakan dikode diatas adalah SetFooterText yang berguna mengeset teks di footer file PDF yang dihasilkan.



    4. fix_url, dengan cuplikan kodenya seperti berikut:



        $purl = parse_url($this->url);

        $surl = parse_url(html_entity_decode($url));



        if ($url == '' OR $url == '#')

            return $this->url . $url;



        if ( ! isset($surl['scheme'])) {

            if ( ! isset($surl['host'])) {

                $split         = explode('/', $surl['path']);

                if (isset($purl['path']) && dirname($purl['path']) != '.')

                    array_unshift($split, ltrim(dirname($purl['path']), '/'));



                $query       = ( ! isset($surl['query'])) ? '' : '?' . $surl['query'];

                $frag        = ( ! isset($surl['fragment'])) ? '' : '#' . $surl['fragment'];



                if ($split[0] == '.') array_shift($split);

                while ($split[0] == '..')

                    @array_shift($split);



                return $purl['scheme'] . '://' . $purl['host'] . '/' . implode('/', $split) . $query . $frag;

            } else

                return $purl['scheme'] . '://' . $url;

        } else return $url;



Jujur saja aku agak kurang yakin dengan "kedigdayaan" method ini jadi beri aku usul jika ada yang lebih baik ataupun kamu menemukan kesalahan fatal disitu. Yang menjadi titik berat penggunaan fungsi ini adalah mengubah url tanpa scheme dan host (atau yang menggunakan tanda ./, ../) menjadi absolute path sehingga dokumen PDF yang kita hasilkan menjadi lebih valid baik link maupun image yang dimasukkan. Sekali lagi, bila kamu sering mengalami kegagalan bila menggunakan image maka ada baiknya kamu mendisable image dengan method "with_image(FALSE)".



-------------------



Maka demikianlah, program web2pdf yang kita buat, memang saya tidak menjelaskan kode perbarisnya karena kebetulan kodenya agak banyak, takutnya yang baca jadi bosen dan akhirnya muntah-muntah, nangis darah, kencing manis. Semoga bermanfaat. Sekali lagi jika ingin mendapatkan source lengkapnya (kamu juga tidak perlu mengunduh TCPDF atau JSLoc lagi karena semua sudah ada dan tinggal digunakan), kamu bisa unduh di: http://bit.ly/n0iEhK . Dan untuk contoh program yang sudah online kamu bisa coba disini:http://bit.ly/mTwonU . Sampai jumpa lagi dan bye !!!!