Sunday, December 7, 2025

Pertemuan 15 | Laporan Pdf

 Nama : Naufal Daffa Alfa Zain

 NRP   : 5025241066

 Kelas  : Pemrograman Web B2 

    Pada tugas ke 15 ini saya membangun aplikasi pendaftaran dan daftar siswa berbasis PHP dengan ekspor PDF menggunakan FPDF. Aplikasi terdiri dari form tambah siswa, halaman tampil data, koneksi database, dan script MariaDB lokal.

    Saya menyiapkan skema basis data sekolah_db melalui berkas sekolah_db.sql yang membuat tabel siswa (nama, nis, jurusan, jenis_kelamin, tanggal_lahir, alamat) lengkap dengan data contoh. Untuk koneksi, file koneksi.php mengarahkan PHP ke MariaDB lokal pada port 3308 dengan socket .mysql-local/mysql.sock.

    Halaman tambah_siswa.php menyediakan form pendaftaran dengan validasi server-side dan penyimpanan via prepared statement. Data yang tersimpan ditampilkan di tampil_siswa.php dalam tabel HTML, dengan opsi unduh PDF. Ekspor PDF memanfaatkan FPDF.

    saya menambahkan skrip start_local_mysql.sh untuk menginisialisasi data dir lokal (.mysql-local), menyalakan MariaDB pada port 3308, dan otomatis mengimpor skema jika belum ada.

    Hasil akhirnya: aplikasi bisa menambah siswa, melihat daftar, dan mengekspor PDF dengan identitas sekolah yang sesuai. 

 

 

 



Source Code

sekolah_db.sql

-- Buat database dan tabel untuk data siswa
CREATE DATABASE IF NOT EXISTS sekolah_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE sekolah_db;

DROP TABLE IF EXISTS siswa;
CREATE TABLE siswa (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
nama VARCHAR(100) NOT NULL,
nis VARCHAR(50) NOT NULL,
jurusan VARCHAR(100) NOT NULL,
jenis_kelamin ENUM('Laki-laki','Perempuan') NOT NULL,
tanggal_lahir DATE NOT NULL,
alamat TEXT NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY unique_nis (nis)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Data contoh
INSERT INTO siswa (nama, nis, jurusan, jenis_kelamin, tanggal_lahir, alamat) VALUES
('Ani Wijaya', '2023001', 'Teknik Informatika', 'Perempuan', '2005-04-12', 'Jl. Melati No. 10'),
('Budi Santoso', '2023002', 'Teknik Elektro', 'Laki-laki', '2004-09-21', 'Jl. Anggrek No. 25');


 

start_local_mysql.sh 

#!/usr/bin/env bash
set -e

DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DATA="$DIR/.mysql-local"
SOCKET="$DATA/mysql.sock"
PORT=3308

mkdir -p "$DATA"
[ -d "$DATA/mysql" ] || mariadb-install-db --datadir="$DATA" --auth-root-authentication-method=normal >/dev/null

mysqld --datadir="$DATA" --socket="$SOCKET" --port="$PORT" --bind-address=127.0.0.1 --skip-networking=0 >/tmp/mysqld-local.log 2>&1 &

until mysqladmin --protocol=socket --socket="$SOCKET" -uroot ping >/dev/null 2>&1; do sleep 1; done

mysql --protocol=socket --socket="$SOCKET" -uroot -e "USE sekolah_db" >/dev/null 2>&1 || mysql --protocol=socket --socket="$SOCKET" -uroot < "$DIR/sekolah_db.sql"



 

koneksi.php 

<?php
$dbHost = '127.0.0.1';
$dbPort = 3308;
$dbUser = 'root';
$dbPass = '';
$dbName = 'sekolah_db';
$dbSocket = __DIR__ . '/.mysql-local/mysql.sock';

$conn = new mysqli($dbHost, $dbUser, $dbPass, $dbName, $dbPort, $dbSocket);
if ($conn->connect_error) {
die('Koneksi database gagal: ' . $conn->connect_error);
}

$conn->set_charset('utf8mb4');


 

tambah_siswa.php 

<?php
require_once 'koneksi.php';

$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$nama = trim($_POST['nama'] ?? '');
$nis = trim($_POST['nis'] ?? '');
$jurusan = trim($_POST['jurusan'] ?? '');
$jenisKelamin = trim($_POST['jenis_kelamin'] ?? '');
$tanggalLahir = trim($_POST['tanggal_lahir'] ?? '');
$alamat = trim($_POST['alamat'] ?? '');

if ($nama === '' || $nis === '' || $jurusan === '' || $jenisKelamin === '' || $tanggalLahir === '' || $alamat === '') {
$errors[] = 'Semua kolom wajib diisi.';
}

if (empty($errors)) {
$stmt = $conn->prepare('INSERT INTO siswa (nama, nis, jurusan, jenis_kelamin, tanggal_lahir, alamat) VALUES (?, ?, ?, ?, ?, ?)');
if (!$stmt) {
$errors[] = 'Gagal menyiapkan query: ' . $conn->error;
} else {
$stmt->bind_param('ssssss', $nama, $nis, $jurusan, $jenisKelamin, $tanggalLahir, $alamat);
if ($stmt->execute()) {
header('Location: tampil_siswa.php?status=added');
exit;
} else {
$errors[] = 'Gagal menyimpan data: ' . $stmt->error;
}
$stmt->close();
}
}
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Tambah Siswa</title>
<style>
body { font-family: Arial, sans-serif; margin: 24px; background: #f6f8fb; }
.container { max-width: 640px; margin: 0 auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08); }
h1 { margin-top: 0; }
label { display: block; margin-bottom: 6px; font-weight: bold; }
input, select, textarea { width: 100%; padding: 10px; margin-bottom: 14px; border: 1px solid #d3d7de; border-radius: 4px; }
button { padding: 10px 16px; background: #1976d2; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
button:hover { background: #135da3; }
.error { background: #ffe0e0; color: #c62828; padding: 10px; border-radius: 4px; margin-bottom: 16px; }
.actions { display: flex; gap: 10px; align-items: center; }
a { color: #1976d2; text-decoration: none; }
</style>
</head>
<body>
<div class="container">
<h1>Form Pendaftaran Siswa</h1>

<?php if (!empty($errors)) : ?>
<div class="error">
<?php foreach ($errors as $err) : ?>
<div><?php echo htmlspecialchars($err); ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>

<form method="post" action="">
<label for="nama">Nama Lengkap</label>
<input type="text" id="nama" name="nama" value="<?php echo htmlspecialchars($_POST['nama'] ?? ''); ?>" required>

<label for="nis">NIS</label>
<input type="text" id="nis" name="nis" value="<?php echo htmlspecialchars($_POST['nis'] ?? ''); ?>" required>

<label for="jurusan">Jurusan</label>
<input type="text" id="jurusan" name="jurusan" value="<?php echo htmlspecialchars($_POST['jurusan'] ?? ''); ?>" required>

<label for="jenis_kelamin">Jenis Kelamin</label>
<select id="jenis_kelamin" name="jenis_kelamin" required>
<option value="">-- Pilih --</option>
<option value="Laki-laki" <?php echo (($_POST['jenis_kelamin'] ?? '') === 'Laki-laki') ? 'selected' : ''; ?>>Laki-laki</option>
<option value="Perempuan" <?php echo (($_POST['jenis_kelamin'] ?? '') === 'Perempuan') ? 'selected' : ''; ?>>Perempuan</option>
</select>

<label for="tanggal_lahir">Tanggal Lahir</label>
<input type="date" id="tanggal_lahir" name="tanggal_lahir" value="<?php echo htmlspecialchars($_POST['tanggal_lahir'] ?? ''); ?>" required>

<label for="alamat">Alamat</label>
<textarea id="alamat" name="alamat" rows="3" required><?php echo htmlspecialchars($_POST['alamat'] ?? ''); ?></textarea>

<div class="actions">
<button type="submit">Simpan</button>
<a href="tampil_siswa.php">Kembali ke Data Siswa</a>
</div>
</form>
</div>
</body>
</html>


 

tampil_siswa.php 

<?php
require_once 'koneksi.php';

function getSiswa($conn)
{
$result = $conn->query('SELECT * FROM siswa ORDER BY id DESC');
if (!$result) {
die('Gagal mengambil data: ' . $conn->error);
}
return $result->fetch_all(MYSQLI_ASSOC);
}

$toIso = function ($text) {
$text = (string) $text;
if (function_exists('mb_convert_encoding')) {
return mb_convert_encoding($text, 'ISO-8859-1', 'UTF-8');
}
if (function_exists('iconv')) {
return iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $text);
}
return $text;
};

$siswa = getSiswa($conn);

if (isset($_GET['export']) && $_GET['export'] === 'pdf') {
require_once 'fpdf.php';

class PDF extends FPDF
{
function Header()
{
$this->SetFont('Arial', 'B', 14);
$this->Cell(0, 7, 'SMK Negeri 2 Fantasi', 0, 1, 'C');
$this->SetFont('Arial', '', 10);
$this->Cell(0, 5, 'Daftar Siswa Kompetensi Keahlian: Rekayasa Perangkat Lunak', 0, 1, 'C');
$this->SetLineWidth(0.6);
$this->Line(10, 25, 200, 25);
$this->SetLineWidth(0);
$this->Ln(10);
}

function Footer()
{
$this->SetY(-15);
$this->SetFont('Arial', 'I', 8);
$this->Cell(0, 10, 'Halaman ' . $this->PageNo() . ' - Data Database: sekolah_db', 0, 0, 'C');
}
}

$pdf = new PDF('P', 'mm', 'A4');
$pdf->AddPage();

$pdf->SetFont('Arial', 'B', 12);
$pdf->Cell(0, 10, 'DATA SISWA JURUSAN RPL', 0, 1, 'C');
$pdf->Ln(2);

$pdf->SetFont('Arial', 'B', 9);
$pdf->SetFillColor(44, 62, 80);
$pdf->SetTextColor(255, 255, 255);

$w = array(10, 25, 60, 30, 28, 37); // lebar kolom: no, nis, nama, jurusan, gender, tgl lahir+alamat

$pdf->Cell($w[0], 8, 'NO', 1, 0, 'C', true);
$pdf->Cell($w[1], 8, 'NIS', 1, 0, 'C', true);
$pdf->Cell($w[2], 8, 'NAMA SISWA', 1, 0, 'L', true);
$pdf->Cell($w[3], 8, 'JURUSAN', 1, 0, 'C', true);
$pdf->Cell($w[4], 8, 'JENIS KEL', 1, 0, 'C', true);
$pdf->Cell($w[5], 8, 'TGL LAHIR', 1, 1, 'C', true);

$pdf->SetFont('Arial', '', 9);
$pdf->SetTextColor(0, 0, 0);
$pdf->SetFillColor(235, 236, 240);

$no = 1;
$fill = false;
foreach ($siswa as $row) {
$pdf->Cell($w[0], 7, $no++, 1, 0, 'C', $fill);
$pdf->Cell($w[1], 7, $row['nis'], 1, 0, 'C', $fill);
$pdf->Cell($w[2], 7, ' ' . strtoupper($toIso($row['nama'])), 1, 0, 'L', $fill);
$pdf->Cell($w[3], 7, $toIso($row['jurusan']), 1, 0, 'C', $fill);
$pdf->Cell($w[4], 7, $toIso($row['jenis_kelamin']), 1, 0, 'C', $fill);
$pdf->Cell($w[5], 7, $row['tanggal_lahir'], 1, 1, 'C', $fill);
$fill = !$fill;
}

$pdf->Output('I', 'data_siswa.pdf');
exit;
}

$status = $_GET['status'] ?? '';
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Data Siswa</title>
<style>
body { font-family: Arial, sans-serif; margin: 24px; background: #f2f5f9; }
.container { max-width: 900px; margin: 0 auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08); }
h1 { margin-top: 0; }
table { border-collapse: collapse; width: 100%; margin-top: 16px; }
th, td { border: 1px solid #d3d7de; padding: 10px; text-align: left; }
th { background: #f0f2f7; }
.actions { margin-top: 12px; display: flex; gap: 10px; }
a.button { display: inline-block; padding: 10px 14px; background: #1976d2; color: #fff; border-radius: 4px; text-decoration: none; }
a.button.secondary { background: #455a64; }
.alert { background: #e8f5e9; color: #2e7d32; padding: 10px; border-radius: 4px; margin-top: 10px; }
</style>
</head>
<body>
<div class="container">
<h1>Data Siswa</h1>

<?php if ($status === 'added'): ?>
<div class="alert">Data siswa berhasil disimpan.</div>
<?php endif; ?>

<div class="actions">
<a class="button" href="tambah_siswa.php">+ Tambah Siswa</a>
<a class="button secondary" href="?export=pdf">Download PDF</a>
</div>

<table>
<thead>
<tr>
<th>No</th>
<th>NIS</th>
<th>Nama</th>
<th>Jurusan</th>
<th>Jenis Kelamin</th>
<th>Tanggal Lahir</th>
<th>Alamat</th>
</tr>
</thead>
<tbody>
<?php if (count($siswa) === 0): ?>
<tr>
<td colspan="7" style="text-align:center;">Belum ada data.</td>
</tr>
<?php else: ?>
<?php foreach ($siswa as $index => $row): ?>
<tr>
<td><?php echo $index + 1; ?></td>
<td><?php echo htmlspecialchars($row['nis']); ?></td>
<td><?php echo htmlspecialchars($row['nama']); ?></td>
<td><?php echo htmlspecialchars($row['jurusan']); ?></td>
<td><?php echo htmlspecialchars($row['jenis_kelamin']); ?></td>
<td><?php echo htmlspecialchars($row['tanggal_lahir']); ?></td>
<td><?php echo htmlspecialchars($row['alamat']); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</body>
</html>


 

fpdf.php 

<?php
/*******************************************************************************
* FPDF *
* *
* Version: 1.86 *
* Date: 2023-06-25 *
* Author: Olivier PLATHEY *
*******************************************************************************/

class FPDF
{
const VERSION = '1.86';
protected $page; // current page number
protected $n; // current object number
protected $offsets; // array of object offsets
protected $buffer; // buffer holding in-memory PDF
protected $pages; // array containing pages
protected $state; // current document state
protected $compress; // compression flag
protected $iconv; // whether iconv is available
protected $k; // scale factor (number of points in user unit)
protected $DefOrientation; // default orientation
protected $CurOrientation; // current orientation
protected $StdPageSizes; // standard page sizes
protected $DefPageSize; // default page size
protected $CurPageSize; // current page size
protected $CurRotation; // current page rotation
protected $PageInfo; // page-related data
protected $wPt, $hPt; // dimensions of current page in points
protected $w, $h; // dimensions of current page in user unit
protected $lMargin; // left margin
protected $tMargin; // top margin
protected $rMargin; // right margin
protected $bMargin; // page break margin
protected $cMargin; // cell margin
protected $x, $y; // current position in user unit
protected $lasth; // height of last printed cell
protected $LineWidth; // line width in user unit
protected $fontpath; // directory containing fonts
protected $CoreFonts; // array of core font names
protected $fonts; // array of used fonts
protected $FontFiles; // array of font files
protected $encodings; // array of encodings
protected $cmaps; // array of ToUnicode CMaps
protected $FontFamily; // current font family
protected $FontStyle; // current font style
protected $underline; // underlining flag
protected $CurrentFont; // current font info
protected $FontSizePt; // current font size in points
protected $FontSize; // current font size in user unit
protected $DrawColor; // commands for drawing color
protected $FillColor; // commands for filling color
protected $TextColor; // commands for text color
protected $ColorFlag; // indicates whether fill and text colors are different
protected $WithAlpha; // indicates whether alpha channel is used
protected $ws; // word spacing
protected $images; // array of used images
protected $PageLinks; // array of links in pages
protected $links; // array of internal links
protected $AutoPageBreak; // automatic page breaking
protected $PageBreakTrigger; // threshold used to trigger page breaks
protected $InHeader; // flag set when processing header
protected $InFooter; // flag set when processing footer
protected $AliasNbPages; // alias for total number of pages
protected $ZoomMode; // zoom display mode
protected $LayoutMode; // layout display mode
protected $metadata; // document properties
protected $CreationDate; // document creation date
protected $PDFVersion; // PDF version number

/*******************************************************************************
* Public methods *
*******************************************************************************/

function __construct($orientation='P', $unit='mm', $size='A4')
{
// Initialization of properties
$this->state = 0;
$this->page = 0;
$this->n = 2;
$this->buffer = '';
$this->pages = array();
$this->PageInfo = array();
$this->fonts = array();
$this->FontFiles = array();
$this->encodings = array();
$this->cmaps = array();
$this->images = array();
$this->links = array();
$this->InHeader = false;
$this->InFooter = false;
$this->lasth = 0;
$this->FontFamily = '';
$this->FontStyle = '';
$this->FontSizePt = 12;
$this->underline = false;
$this->DrawColor = '0 G';
$this->FillColor = '0 g';
$this->TextColor = '0 g';
$this->ColorFlag = false;
$this->WithAlpha = false;
$this->ws = 0;
$this->iconv = function_exists('iconv');
// Font path
if(defined('FPDF_FONTPATH'))
$this->fontpath = FPDF_FONTPATH;
else
$this->fontpath = dirname(__FILE__).'/font/';
// Core fonts
$this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats');
// Scale factor
if($unit=='pt')
$this->k = 1;
elseif($unit=='mm')
$this->k = 72/25.4;
elseif($unit=='cm')
$this->k = 72/2.54;
elseif($unit=='in')
$this->k = 72;
else
$this->Error('Incorrect unit: '.$unit);
// Page sizes
$this->StdPageSizes = array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28),
'letter'=>array(612,792), 'legal'=>array(612,1008));
$size = $this->_getpagesize($size);
$this->DefPageSize = $size;
$this->CurPageSize = $size;
// Page orientation
$orientation = strtolower($orientation);
if($orientation=='p' || $orientation=='portrait')
{
$this->DefOrientation = 'P';
$this->w = $size[0];
$this->h = $size[1];
}
elseif($orientation=='l' || $orientation=='landscape')
{
$this->DefOrientation = 'L';
$this->w = $size[1];
$this->h = $size[0];
}
else
$this->Error('Incorrect orientation: '.$orientation);
$this->CurOrientation = $this->DefOrientation;
$this->wPt = $this->w*$this->k;
$this->hPt = $this->h*$this->k;
// Page rotation
$this->CurRotation = 0;
// Page margins (1 cm)
$margin = 28.35/$this->k;
$this->SetMargins($margin,$margin);
// Interior cell margin (1 mm)
$this->cMargin = $margin/10;
// Line width (0.2 mm)
$this->LineWidth = .567/$this->k;
// Automatic page break
$this->SetAutoPageBreak(true,2*$margin);
// Default display mode
$this->SetDisplayMode('default');
// Enable compression
$this->SetCompression(true);
// Metadata
$this->metadata = array('Producer'=>'FPDF '.self::VERSION);
// Set default PDF version number
$this->PDFVersion = '1.3';
}

function SetMargins($left, $top, $right=null)
{
// Set left, top and right margins
$this->lMargin = $left;
$this->tMargin = $top;
if($right===null)
$right = $left;
$this->rMargin = $right;
}

function SetLeftMargin($margin)
{
// Set left margin
$this->lMargin = $margin;
if($this->page>0 && $this->x<$margin)
$this->x = $margin;
}

function SetTopMargin($margin)
{
// Set top margin
$this->tMargin = $margin;
}

function SetRightMargin($margin)
{
// Set right margin
$this->rMargin = $margin;
}

function SetAutoPageBreak($auto, $margin=0)
{
// Set auto page break mode and triggering margin
$this->AutoPageBreak = $auto;
$this->bMargin = $margin;
$this->PageBreakTrigger = $this->h-$margin;
}

function SetDisplayMode($zoom, $layout='default')
{
// Set display mode in viewer
if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom))
$this->ZoomMode = $zoom;
else
$this->Error('Incorrect zoom display mode: '.$zoom);
if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default')
$this->LayoutMode = $layout;
else
$this->Error('Incorrect layout display mode: '.$layout);
}

function SetCompression($compress)
{
// Set page compression
if(function_exists('gzcompress'))
$this->compress = $compress;
else
$this->compress = false;
}

function SetTitle($title, $isUTF8=false)
{
// Title of document
$this->metadata['Title'] = $isUTF8 ? $title : $this->_UTF8encode($title);
}

function SetAuthor($author, $isUTF8=false)
{
// Author of document
$this->metadata['Author'] = $isUTF8 ? $author : $this->_UTF8encode($author);
}

function SetSubject($subject, $isUTF8=false)
{
// Subject of document
$this->metadata['Subject'] = $isUTF8 ? $subject : $this->_UTF8encode($subject);
}

function SetKeywords($keywords, $isUTF8=false)
{
// Keywords of document
$this->metadata['Keywords'] = $isUTF8 ? $keywords : $this->_UTF8encode($keywords);
}

function SetCreator($creator, $isUTF8=false)
{
// Creator of document
$this->metadata['Creator'] = $isUTF8 ? $creator : $this->_UTF8encode($creator);
}

function AliasNbPages($alias='{nb}')
{
// Define an alias for total number of pages
$this->AliasNbPages = $alias;
}

function Error($msg)
{
// Fatal error
throw new Exception('FPDF error: '.$msg);
}

function Close()
{
// Terminate document
if($this->state==3)
return;
if($this->page==0)
$this->AddPage();
// Page footer
$this->InFooter = true;
$this->Footer();
$this->InFooter = false;
// Close page
$this->_endpage();
// Close document
$this->_enddoc();
}

function AddPage($orientation='', $size='', $rotation=0)
{
// Start a new page
if($this->state==3)
$this->Error('The document is closed');
$family = $this->FontFamily;
$style = $this->FontStyle.($this->underline ? 'U' : '');
$fontsize = $this->FontSizePt;
$lw = $this->LineWidth;
$dc = $this->DrawColor;
$fc = $this->FillColor;
$tc = $this->TextColor;
$cf = $this->ColorFlag;
if($this->page>0)
{
// Page footer
$this->InFooter = true;
$this->Footer();
$this->InFooter = false;
// Close page
$this->_endpage();
}
// Start new page
$this->_beginpage($orientation,$size,$rotation);
// Set line cap style to square
$this->_out('2 J');
// Set line width
$this->LineWidth = $lw;
$this->_out(sprintf('%.2F w',$lw*$this->k));
// Set font
if($family)
$this->SetFont($family,$style,$fontsize);
// Set colors
$this->DrawColor = $dc;
if($dc!='0 G')
$this->_out($dc);
$this->FillColor = $fc;
if($fc!='0 g')
$this->_out($fc);
$this->TextColor = $tc;
$this->ColorFlag = $cf;
// Page header
$this->InHeader = true;
$this->Header();
$this->InHeader = false;
// Restore line width
if($this->LineWidth!=$lw)
{
$this->LineWidth = $lw;
$this->_out(sprintf('%.2F w',$lw*$this->k));
}
// Restore font
if($family)
$this->SetFont($family,$style,$fontsize);
// Restore colors
if($this->DrawColor!=$dc)
{
$this->DrawColor = $dc;
$this->_out($dc);
}
if($this->FillColor!=$fc)
{
$this->FillColor = $fc;
$this->_out($fc);
}
$this->TextColor = $tc;
$this->ColorFlag = $cf;
}

function Header()
{
// To be implemented in your own inherited class
}

function Footer()
{
// To be implemented in your own inherited class
}

function PageNo()
{
// Get current page number
return $this->page;
}

function SetDrawColor($r, $g=null, $b=null)
{
// Set color for all stroking operations
if(($r==0 && $g==0 && $b==0) || $g===null)
$this->DrawColor = sprintf('%.3F G',$r/255);
else
$this->DrawColor = sprintf('%.3F %.3F %.3F RG',$r/255,$g/255,$b/255);
if($this->page>0)
$this->_out($this->DrawColor);
}

function SetFillColor($r, $g=null, $b=null)
{
// Set color for all filling operations
if(($r==0 && $g==0 && $b==0) || $g===null)
$this->FillColor = sprintf('%.3F g',$r/255);
else
$this->FillColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255);
$this->ColorFlag = ($this->FillColor!=$this->TextColor);
if($this->page>0)
$this->_out($this->FillColor);
}

function SetTextColor($r, $g=null, $b=null)
{
// Set color for text
if(($r==0 && $g==0 && $b==0) || $g===null)
$this->TextColor = sprintf('%.3F g',$r/255);
else
$this->TextColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255);
$this->ColorFlag = ($this->FillColor!=$this->TextColor);
}

function GetStringWidth($s)
{
// Get width of a string in the current font
$cw = $this->CurrentFont['cw'];
$w = 0;
$s = (string)$s;
$l = strlen($s);
for($i=0;$i<$l;$i++)
$w += $cw[$s[$i]];
return $w*$this->FontSize/1000;
}

function SetLineWidth($width)
{
// Set line width
$this->LineWidth = $width;
if($this->page>0)
$this->_out(sprintf('%.2F w',$width*$this->k));
}

function Line($x1, $y1, $x2, $y2)
{
// Draw a line
$this->_out(sprintf('%.2F %.2F m %.2F %.2F l S',$x1*$this->k,($this->h-$y1)*$this->k,$x2*$this->k,($this->h-$y2)*$this->k));
}

function Rect($x, $y, $w, $h, $style='')
{
// Draw a rectangle
if($style=='F')
$op = 'f';
elseif($style=='FD' || $style=='DF')
$op = 'B';
else
$op = 'S';
$this->_out(sprintf('%.2F %.2F %.2F %.2F re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op));
}

function AddFont($family, $style='', $file='', $dir='')
{
// Add a TrueType, OpenType or Type1 font
$family = strtolower($family);
if($file=='')
$file = str_replace(' ','',$family).strtolower($style).'.php';
$style = strtoupper($style);
if($style=='IB')
$style = 'BI';
$fontkey = $family.$style;
if(isset($this->fonts[$fontkey]))
return;
if(strpos($file,'/')!==false || strpos($file,"\\")!==false)
$this->Error('Incorrect font definition file name: '.$file);
if($dir=='')
$dir = $this->fontpath;
if(substr($dir,-1)!='/' && substr($dir,-1)!='\\')
$dir .= '/';
$info = $this->_loadfont($dir.$file);
$info['i'] = count($this->fonts)+1;
if(!empty($info['file']))
{
// Embedded font
$info['file'] = $dir.$info['file'];
if($info['type']=='TrueType')
$this->FontFiles[$info['file']] = array('length1'=>$info['originalsize']);
else
$this->FontFiles[$info['file']] = array('length1'=>$info['size1'], 'length2'=>$info['size2']);
}
$this->fonts[$fontkey] = $info;
}

function SetFont($family, $style='', $size=0)
{
// Select a font; size given in points
if($family=='')
$family = $this->FontFamily;
else
$family = strtolower($family);
$style = strtoupper($style);
if(strpos($style,'U')!==false)
{
$this->underline = true;
$style = str_replace('U','',$style);
}
else
$this->underline = false;
if($style=='IB')
$style = 'BI';
if($size==0)
$size = $this->FontSizePt;
// Test if font is already selected
if($this->FontFamily==$family && $this->FontStyle==$style && $this->FontSizePt==$size)
return;
// Test if font is already loaded
$fontkey = $family.$style;
if(!isset($this->fonts[$fontkey]))
{
// Test if one of the core fonts
if($family=='arial')
$family = 'helvetica';
if(in_array($family,$this->CoreFonts))
{
if($family=='symbol' || $family=='zapfdingbats')
$style = '';
$fontkey = $family.$style;
if(!isset($this->fonts[$fontkey]))
$this->AddFont($family,$style);
}
else
$this->Error('Undefined font: '.$family.' '.$style);
}
// Select it
$this->FontFamily = $family;
$this->FontStyle = $style;
$this->FontSizePt = $size;
$this->FontSize = $size/$this->k;
$this->CurrentFont = $this->fonts[$fontkey];
if($this->page>0)
$this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
}

function SetFontSize($size)
{
// Set font size in points
if($this->FontSizePt==$size)
return;
$this->FontSizePt = $size;
$this->FontSize = $size/$this->k;
if($this->page>0 && isset($this->CurrentFont))
$this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
}

function AddLink()
{
// Create a new internal link
$n = count($this->links)+1;
$this->links[$n] = array(0, 0);
return $n;
}

function SetLink($link, $y=0, $page=-1)
{
// Set destination of internal link
if($y==-1)
$y = $this->y;
if($page==-1)
$page = $this->page;
$this->links[$link] = array($page, $y);
}

function Link($x, $y, $w, $h, $link)
{
// Put a link on the page
$this->PageLinks[$this->page][] = array($x*$this->k, $this->hPt-$y*$this->k, $w*$this->k, $h*$this->k, $link);
}

function Text($x, $y, $txt)
{
// Output a string
if(!isset($this->CurrentFont))
$this->Error('No font has been set');
$txt = (string)$txt;
$s = sprintf('BT %.2F %.2F Td (%s) Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escape($txt));
if($this->underline && $txt!=='')
$s .= ' '.$this->_dounderline($x,$y,$txt);
if($this->ColorFlag)
$s = 'q '.$this->TextColor.' '.$s.' Q';
$this->_out($s);
}

function AcceptPageBreak()
{
// Accept automatic page break or not
return $this->AutoPageBreak;
}

function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='')
{
// Output a cell
$k = $this->k;
if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
{
// Automatic page break
$x = $this->x;
$ws = $this->ws;
if($ws>0)
{
$this->ws = 0;
$this->_out('0 Tw');
}
$this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation);
$this->x = $x;
if($ws>0)
{
$this->ws = $ws;
$this->_out(sprintf('%.3F Tw',$ws*$k));
}
}
if($w==0)
$w = $this->w-$this->rMargin-$this->x;
$s = '';
if($fill || $border==1)
{
if($fill)
$op = ($border==1) ? 'B' : 'f';
else
$op = 'S';
$s = sprintf('%.2F %.2F %.2F %.2F re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op);
}
if(is_string($border))
{
$x = $this->x;
$y = $this->y;
if(strpos($border,'L')!==false)
$s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k);
if(strpos($border,'T')!==false)
$s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k);
if(strpos($border,'R')!==false)
$s .= sprintf('%.2F %.2F m %.2F %.2F l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
if(strpos($border,'B')!==false)
$s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
}
$txt = (string)$txt;
if($txt!=='')
{
if(!isset($this->CurrentFont))
$this->Error('No font has been set');
if($align=='R')
$dx = $w-$this->cMargin-$this->GetStringWidth($txt);
elseif($align=='C')
$dx = ($w-$this->GetStringWidth($txt))/2;
else
$dx = $this->cMargin;
if($this->ColorFlag)
$s .= 'q '.$this->TextColor.' ';
$s .= sprintf('BT %.2F %.2F Td (%s) Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$this->_escape($txt));
if($this->underline)
$s .= ' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt);
if($this->ColorFlag)
$s .= ' Q';
if($link)
$this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link);
}
if($s)
$this->_out($s);
$this->lasth = $h;
if($ln>0)
{
// Go to next line
$this->y += $h;
if($ln==1)
$this->x = $this->lMargin;
}
else
$this->x += $w;
}

function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false)
{
// Output text with automatic or explicit line breaks
if(!isset($this->CurrentFont))
$this->Error('No font has been set');
$cw = $this->CurrentFont['cw'];
if($w==0)
$w = $this->w-$this->rMargin-$this->x;
$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
$s = str_replace("\r",'',(string)$txt);
$nb = strlen($s);
if($nb>0 && $s[$nb-1]=="\n")
$nb--;
$b = 0;
if($border)
{
if($border==1)
{
$border = 'LTRB';
$b = 'LRT';
$b2 = 'LR';
}
else
{
$b2 = '';
if(strpos($border,'L')!==false)
$b2 .= 'L';
if(strpos($border,'R')!==false)
$b2 .= 'R';
$b = (strpos($border,'T')!==false) ? $b2.'T' : $b2;
}
}
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$ns = 0;
$nl = 1;
while($i<$nb)
{
// Get next character
$c = $s[$i];
if($c=="\n")
{
// Explicit line break
if($this->ws>0)
{
$this->ws = 0;
$this->_out('0 Tw');
}
$this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
$i++;
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
if($border && $nl==2)
$b = $b2;
continue;
}
if($c==' ')
{
$sep = $i;
$ls = $l;
$ns++;
}
$l += $cw[$c];
if($l>$wmax)
{
// Automatic line break
if($sep==-1)
{
if($i==$j)
$i++;
if($this->ws>0)
{
$this->ws = 0;
$this->_out('0 Tw');
}
$this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
}
else
{
if($align=='J')
{
$this->ws = ($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0;
$this->_out(sprintf('%.3F Tw',$this->ws*$this->k));
}
$this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);
$i = $sep+1;
}
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
if($border && $nl==2)
$b = $b2;
}
else
$i++;
}
// Last chunk
if($this->ws>0)
{
$this->ws = 0;
$this->_out('0 Tw');
}
if($border && strpos($border,'B')!==false)
$b .= 'B';
$this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
$this->x = $this->lMargin;
}

function Write($h, $txt, $link='')
{
// Output text in flowing mode
if(!isset($this->CurrentFont))
$this->Error('No font has been set');
$cw = $this->CurrentFont['cw'];
$w = $this->w-$this->rMargin-$this->x;
$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
$s = str_replace("\r",'',(string)$txt);
$nb = strlen($s);
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$nl = 1;
while($i<$nb)
{
// Get next character
$c = $s[$i];
if($c=="\n")
{
// Explicit line break
$this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link);
$i++;
$sep = -1;
$j = $i;
$l = 0;
if($nl==1)
{
$this->x = $this->lMargin;
$w = $this->w-$this->rMargin-$this->x;
$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
}
$nl++;
continue;
}
if($c==' ')
$sep = $i;
$l += $cw[$c];
if($l>$wmax)
{
// Automatic line break
if($sep==-1)
{
if($this->x>$this->lMargin)
{
// Move to next line
$this->x = $this->lMargin;
$this->y += $h;
$w = $this->w-$this->rMargin-$this->x;
$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
$i++;
$nl++;
continue;
}
if($i==$j)
$i++;
$this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link);
}
else
{
$this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',false,$link);
$i = $sep+1;
}
$sep = -1;
$j = $i;
$l = 0;
if($nl==1)
{
$this->x = $this->lMargin;
$w = $this->w-$this->rMargin-$this->x;
$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
}
$nl++;
}
else
$i++;
}
// Last chunk
if($i!=$j)
$this->Cell($l/1000*$this->FontSize,$h,substr($s,$j),0,0,'',false,$link);
}

function Ln($h=null)
{
// Line feed; default value is the last cell height
$this->x = $this->lMargin;
if($h===null)
$this->y += $this->lasth;
else
$this->y += $h;
}

function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='')
{
// Put an image on the page
if($file=='')
$this->Error('Image file name is empty');
if(!isset($this->images[$file]))
{
// First use of this image, get info
if($type=='')
{
$pos = strrpos($file,'.');
if(!$pos)
$this->Error('Image file has no extension and no type was specified: '.$file);
$type = substr($file,$pos+1);
}
$type = strtolower($type);
if($type=='jpeg')
$type = 'jpg';
$mtd = '_parse'.$type;
if(!method_exists($this,$mtd))
$this->Error('Unsupported image type: '.$type);
$info = $this->$mtd($file);
$info['i'] = count($this->images)+1;
$this->images[$file] = $info;
}
else
$info = $this->images[$file];

// Automatic width and height calculation if needed
if($w==0 && $h==0)
{
// Put image at 96 dpi
$w = -96;
$h = -96;
}
if($w<0)
$w = -$info['w']*72/$w/$this->k;
if($h<0)
$h = -$info['h']*72/$h/$this->k;
if($w==0)
$w = $h*$info['w']/$info['h'];
if($h==0)
$h = $w*$info['h']/$info['w'];

// Flowing mode
if($y===null)
{
if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
{
// Automatic page break
$x2 = $this->x;
$this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation);
$this->x = $x2;
}
$y = $this->y;
$this->y += $h;
}

if($x===null)
$x = $this->x;
$this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q',$w*$this->k,$h*$this->k,$x*$this->k,($this->h-($y+$h))*$this->k,$info['i']));
if($link)
$this->Link($x,$y,$w,$h,$link);
}

function GetPageWidth()
{
// Get current page width
return $this->w;
}

function GetPageHeight()
{
// Get current page height
return $this->h;
}

function GetX()
{
// Get x position
return $this->x;
}

function SetX($x)
{
// Set x position
if($x>=0)
$this->x = $x;
else
$this->x = $this->w+$x;
}

function GetY()
{
// Get y position
return $this->y;
}

function SetY($y, $resetX=true)
{
// Set y position and optionally reset x
if($y>=0)
$this->y = $y;
else
$this->y = $this->h+$y;
if($resetX)
$this->x = $this->lMargin;
}

function SetXY($x, $y)
{
// Set x and y positions
$this->SetX($x);
$this->SetY($y,false);
}

function Output($dest='', $name='', $isUTF8=false)
{
// Output PDF to some destination
$this->Close();
if(strlen($name)==1 && strlen($dest)!=1)
{
// Fix parameter order
$tmp = $dest;
$dest = $name;
$name = $tmp;
}
if($dest=='')
$dest = 'I';
if($name=='')
$name = 'doc.pdf';
switch(strtoupper($dest))
{
case 'I':
// Send to standard output
$this->_checkoutput();
if(PHP_SAPI!='cli')
{
// We send to a browser
header('Content-Type: application/pdf');
header('Content-Disposition: inline; '.$this->_httpencode('filename',$name,$isUTF8));
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
}
echo $this->buffer;
break;
case 'D':
// Download file
$this->_checkoutput();
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; '.$this->_httpencode('filename',$name,$isUTF8));
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
echo $this->buffer;
break;
case 'F':
// Save to local file
if(!file_put_contents($name,$this->buffer))
$this->Error('Unable to create output file: '.$name);
break;
case 'S':
// Return as a string
return $this->buffer;
default:
$this->Error('Incorrect output destination: '.$dest);
}
return '';
}

/*******************************************************************************
* Protected methods *
*******************************************************************************/

protected function _checkoutput()
{
if(PHP_SAPI!='cli')
{
if(headers_sent($file,$line))
$this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)");
}
if(ob_get_length())
{
// The output buffer is not empty
if(preg_match('/^(\xEF\xBB\xBF)?\s*$/',ob_get_contents()))
{
// It contains only a UTF-8 BOM and/or whitespace, let's clean it
ob_clean();
}
else
$this->Error("Some data has already been output, can't send PDF file");
}
}

protected function _getpagesize($size)
{
if(is_string($size))
{
$size = strtolower($size);
if(!isset($this->StdPageSizes[$size]))
$this->Error('Unknown page size: '.$size);
$a = $this->StdPageSizes[$size];
return array($a[0]/$this->k, $a[1]/$this->k);
}
else
{
if($size[0]>$size[1])
return array($size[1], $size[0]);
else
return $size;
}
}

protected function _beginpage($orientation, $size, $rotation)
{
$this->page++;
$this->pages[$this->page] = '';
$this->PageLinks[$this->page] = array();
$this->state = 2;
$this->x = $this->lMargin;
$this->y = $this->tMargin;
$this->FontFamily = '';
// Check page size and orientation
if($orientation=='')
$orientation = $this->DefOrientation;
else
$orientation = strtoupper($orientation[0]);
if($size=='')
$size = $this->DefPageSize;
else
$size = $this->_getpagesize($size);
if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1])
{
// New size or orientation
if($orientation=='P')
{
$this->w = $size[0];
$this->h = $size[1];
}
else
{
$this->w = $size[1];
$this->h = $size[0];
}
$this->wPt = $this->w*$this->k;
$this->hPt = $this->h*$this->k;
$this->PageBreakTrigger = $this->h-$this->bMargin;
$this->CurOrientation = $orientation;
$this->CurPageSize = $size;
}
if($orientation!=$this->DefOrientation || $size[0]!=$this->DefPageSize[0] || $size[1]!=$this->DefPageSize[1])
$this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt);
if($rotation!=0)
{
if($rotation%90!=0)
$this->Error('Incorrect rotation value: '.$rotation);
$this->PageInfo[$this->page]['rotation'] = $rotation;
}
$this->CurRotation = $rotation;
}

protected function _endpage()
{
$this->state = 1;
}

protected function _loadfont($path)
{
// Load a font definition file
include($path);
if(!isset($name))
$this->Error('Could not include font definition file: '.$path);
if(isset($enc))
$enc = strtolower($enc);
if(!isset($subsetted))
$subsetted = false;
return get_defined_vars();
}

protected function _isascii($s)
{
// Test if string is ASCII
$nb = strlen($s);
for($i=0;$i<$nb;$i++)
{
if(ord($s[$i])>127)
return false;
}
return true;
}

protected function _httpencode($param, $value, $isUTF8)
{
// Encode HTTP header field parameter
if($this->_isascii($value))
return $param.'="'.$value.'"';
if(!$isUTF8)
$value = $this->_UTF8encode($value);
return $param."*=UTF-8''".rawurlencode($value);
}

protected function _UTF8encode($s)
{
// Convert ISO-8859-1 to UTF-8
if($this->iconv)
return iconv('ISO-8859-1','UTF-8',$s);
$res = '';
$nb = strlen($s);
for($i=0;$i<$nb;$i++)
{
$c = $s[$i];
$v = ord($c);
if($v>=128)
{
$res .= chr(0xC0 | ($v >> 6));
$res .= chr(0x80 | ($v & 0x3F));
}
else
$res .= $c;
}
return $res;
}

protected function _UTF8toUTF16($s)
{
// Convert UTF-8 to UTF-16BE with BOM
$res = "\xFE\xFF";
if($this->iconv)
return $res.iconv('UTF-8','UTF-16BE',$s);
$nb = strlen($s);
$i = 0;
while($i<$nb)
{
$c1 = ord($s[$i++]);
if($c1>=224)
{
// 3-byte character
$c2 = ord($s[$i++]);
$c3 = ord($s[$i++]);
$res .= chr((($c1 & 0x0F)<<4) + (($c2 & 0x3C)>>2));
$res .= chr((($c2 & 0x03)<<6) + ($c3 & 0x3F));
}
elseif($c1>=192)
{
// 2-byte character
$c2 = ord($s[$i++]);
$res .= chr(($c1 & 0x1C)>>2);
$res .= chr((($c1 & 0x03)<<6) + ($c2 & 0x3F));
}
else
{
// Single-byte character
$res .= "\0".chr($c1);
}
}
return $res;
}

protected function _escape($s)
{
// Escape special characters
if(strpos($s,'(')!==false || strpos($s,')')!==false || strpos($s,'\\')!==false || strpos($s,"\r")!==false)
return str_replace(array('\\','(',')',"\r"), array('\\\\','\\(','\\)','\\r'), $s);
else
return $s;
}

protected function _textstring($s)
{
// Format a text string
if(!$this->_isascii($s))
$s = $this->_UTF8toUTF16($s);
return '('.$this->_escape($s).')';
}

protected function _dounderline($x, $y, $txt)
{
// Underline text
$up = $this->CurrentFont['up'];
$ut = $this->CurrentFont['ut'];
$w = $this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');
return sprintf('%.2F %.2F %.2F %.2F re f',$x*$this->k,($this->h-($y-$up/1000*$this->FontSize))*$this->k,$w*$this->k,-$ut/1000*$this->FontSizePt);
}

protected function _parsejpg($file)
{
// Extract info from a JPEG file
$a = getimagesize($file);
if(!$a)
$this->Error('Missing or incorrect image file: '.$file);
if($a[2]!=2)
$this->Error('Not a JPEG file: '.$file);
if(!isset($a['channels']) || $a['channels']==3)
$colspace = 'DeviceRGB';
elseif($a['channels']==4)
$colspace = 'DeviceCMYK';
else
$colspace = 'DeviceGray';
$bpc = isset($a['bits']) ? $a['bits'] : 8;
$data = file_get_contents($file);
return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data);
}

protected function _parsepng($file)
{
// Extract info from a PNG file
$f = fopen($file,'rb');
if(!$f)
$this->Error('Can\'t open image file: '.$file);
$info = $this->_parsepngstream($f,$file);
fclose($f);
return $info;
}

protected function _parsepngstream($f, $file)
{
// Check signature
if($this->_readstream($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10))
$this->Error('Not a PNG file: '.$file);

// Read header chunk
$this->_readstream($f,4);
if($this->_readstream($f,4)!='IHDR')
$this->Error('Incorrect PNG file: '.$file);
$w = $this->_readint($f);
$h = $this->_readint($f);
$bpc = ord($this->_readstream($f,1));
if($bpc>8)
$this->Error('16-bit depth not supported: '.$file);
$ct = ord($this->_readstream($f,1));
if($ct==0 || $ct==4)
$colspace = 'DeviceGray';
elseif($ct==2 || $ct==6)
$colspace = 'DeviceRGB';
elseif($ct==3)
$colspace = 'Indexed';
else
$this->Error('Unknown color type: '.$file);
if(ord($this->_readstream($f,1))!=0)
$this->Error('Unknown compression method: '.$file);
if(ord($this->_readstream($f,1))!=0)
$this->Error('Unknown filter method: '.$file);
if(ord($this->_readstream($f,1))!=0)
$this->Error('Interlacing not supported: '.$file);
$this->_readstream($f,4);
$dp = '/Predictor 15 /Colors '.($colspace=='DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w;

// Scan chunks looking for palette, transparency and image data
$pal = '';
$trns = '';
$data = '';
do
{
$n = $this->_readint($f);
$type = $this->_readstream($f,4);
if($type=='PLTE')
{
// Read palette
$pal = $this->_readstream($f,$n);
$this->_readstream($f,4);
}
elseif($type=='tRNS')
{
// Read transparency info
$t = $this->_readstream($f,$n);
if($ct==0)
$trns = array(ord(substr($t,1,1)));
elseif($ct==2)
$trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
else
{
$pos = strpos($t,chr(0));
if($pos!==false)
$trns = array($pos);
}
$this->_readstream($f,4);
}
elseif($type=='IDAT')
{
// Read image data block
$data .= $this->_readstream($f,$n);
$this->_readstream($f,4);
}
elseif($type=='IEND')
break;
else
$this->_readstream($f,$n+4);
}
while($n);

if($colspace=='Indexed' && empty($pal))
$this->Error('Missing palette in '.$file);
$info = array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'dp'=>$dp, 'pal'=>$pal, 'trns'=>$trns);
if($ct>=4)
{
// Extract alpha channel
if(!function_exists('gzuncompress'))
$this->Error('Zlib not available, can\'t handle alpha channel: '.$file);
$data = gzuncompress($data);
$color = '';
$alpha = '';
if($ct==4)
{
// Gray image
$len = 2*$w;
for($i=0;$i<$h;$i++)
{
$pos = (1+$len)*$i;
$color .= $data[$pos];
$alpha .= $data[$pos];
$line = substr($data,$pos+1,$len);
$color .= preg_replace('/(.)./s','$1',$line);
$alpha .= preg_replace('/.(.)/s','$1',$line);
}
}
else
{
// RGB image
$len = 4*$w;
for($i=0;$i<$h;$i++)
{
$pos = (1+$len)*$i;
$color .= $data[$pos];
$alpha .= $data[$pos];
$line = substr($data,$pos+1,$len);
$color .= preg_replace('/(.{3})./s','$1',$line);
$alpha .= preg_replace('/.{3}(.)/s','$1',$line);
}
}
unset($data);
$data = gzcompress($color);
$info['smask'] = gzcompress($alpha);
$this->WithAlpha = true;
if($this->PDFVersion<'1.4')
$this->PDFVersion = '1.4';
}
$info['data'] = $data;
return $info;
}

protected function _readstream($f, $n)
{
// Read n bytes from stream
$res = '';
while($n>0 && !feof($f))
{
$s = fread($f,$n);
if($s===false)
$this->Error('Error while reading stream');
$n -= strlen($s);
$res .= $s;
}
if($n>0)
$this->Error('Unexpected end of stream');
return $res;
}

protected function _readint($f)
{
// Read a 4-byte integer from stream
$a = unpack('Ni',$this->_readstream($f,4));
return $a['i'];
}

protected function _parsegif($file)
{
// Extract info from a GIF file (via PNG conversion)
if(!function_exists('imagepng'))
$this->Error('GD extension is required for GIF support');
if(!function_exists('imagecreatefromgif'))
$this->Error('GD has no GIF read support');
$im = imagecreatefromgif($file);
if(!$im)
$this->Error('Missing or incorrect image file: '.$file);
imageinterlace($im,0);
ob_start();
imagepng($im);
$data = ob_get_clean();
imagedestroy($im);
$f = fopen('php://temp','rb+');
if(!$f)
$this->Error('Unable to create memory stream');
fwrite($f,$data);
rewind($f);
$info = $this->_parsepngstream($f,$file);
fclose($f);
return $info;
}

protected function _out($s)
{
// Add a line to the current page
if($this->state==2)
$this->pages[$this->page] .= $s."\n";
elseif($this->state==0)
$this->Error('No page has been added yet');
elseif($this->state==1)
$this->Error('Invalid call');
elseif($this->state==3)
$this->Error('The document is closed');
}

protected function _put($s)
{
// Add a line to the document
$this->buffer .= $s."\n";
}

protected function _getoffset()
{
return strlen($this->buffer);
}

protected function _newobj($n=null)
{
// Begin a new object
if($n===null)
$n = ++$this->n;
$this->offsets[$n] = $this->_getoffset();
$this->_put($n.' 0 obj');
}

protected function _putstream($data)
{
$this->_put('stream');
$this->_put($data);
$this->_put('endstream');
}

protected function _putstreamobject($data)
{
if($this->compress)
{
$entries = '/Filter /FlateDecode ';
$data = gzcompress($data);
}
else
$entries = '';
$entries .= '/Length '.strlen($data);
$this->_newobj();
$this->_put('<<'.$entries.'>>');
$this->_putstream($data);
$this->_put('endobj');
}

protected function _putlinks($n)
{
foreach($this->PageLinks[$n] as $pl)
{
$this->_newobj();
$rect = sprintf('%.2F %.2F %.2F %.2F',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]);
$s = '<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] ';
if(is_string($pl[4]))
$s .= '/A <</S /URI /URI '.$this->_textstring($pl[4]).'>>>>';
else
{
$l = $this->links[$pl[4]];
if(isset($this->PageInfo[$l[0]]['size']))
$h = $this->PageInfo[$l[0]]['size'][1];
else
$h = ($this->DefOrientation=='P') ? $this->DefPageSize[1]*$this->k : $this->DefPageSize[0]*$this->k;
$s .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>',$this->PageInfo[$l[0]]['n'],$h-$l[1]*$this->k);
}
$this->_put($s);
$this->_put('endobj');
}
}

protected function _putpage($n)
{
$this->_newobj();
$this->_put('<</Type /Page');
$this->_put('/Parent 1 0 R');
if(isset($this->PageInfo[$n]['size']))
$this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->PageInfo[$n]['size'][0],$this->PageInfo[$n]['size'][1]));
if(isset($this->PageInfo[$n]['rotation']))
$this->_put('/Rotate '.$this->PageInfo[$n]['rotation']);
$this->_put('/Resources 2 0 R');
if(!empty($this->PageLinks[$n]))
{
$s = '/Annots [';
foreach($this->PageLinks[$n] as $pl)
$s .= $pl[5].' 0 R ';
$s .= ']';
$this->_put($s);
}
if($this->WithAlpha)
$this->_put('/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>');
$this->_put('/Contents '.($this->n+1).' 0 R>>');
$this->_put('endobj');
// Page content
if(!empty($this->AliasNbPages))
$this->pages[$n] = str_replace($this->AliasNbPages,$this->page,$this->pages[$n]);
$this->_putstreamobject($this->pages[$n]);
// Link annotations
$this->_putlinks($n);
}

protected function _putpages()
{
$nb = $this->page;
$n = $this->n;
for($i=1;$i<=$nb;$i++)
{
$this->PageInfo[$i]['n'] = ++$n;
$n++;
foreach($this->PageLinks[$i] as &$pl)
$pl[5] = ++$n;
unset($pl);
}
for($i=1;$i<=$nb;$i++)
$this->_putpage($i);
// Pages root
$this->_newobj(1);
$this->_put('<</Type /Pages');
$kids = '/Kids [';
for($i=1;$i<=$nb;$i++)
$kids .= $this->PageInfo[$i]['n'].' 0 R ';
$kids .= ']';
$this->_put($kids);
$this->_put('/Count '.$nb);
if($this->DefOrientation=='P')
{
$w = $this->DefPageSize[0];
$h = $this->DefPageSize[1];
}
else
{
$w = $this->DefPageSize[1];
$h = $this->DefPageSize[0];
}
$this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$w*$this->k,$h*$this->k));
$this->_put('>>');
$this->_put('endobj');
}

protected function _putfonts()
{
foreach($this->FontFiles as $file=>$info)
{
// Font file embedding
$this->_newobj();
$this->FontFiles[$file]['n'] = $this->n;
$font = file_get_contents($file);
if(!$font)
$this->Error('Font file not found: '.$file);
$compressed = (substr($file,-2)=='.z');
if(!$compressed && isset($info['length2']))
$font = substr($font,6,$info['length1']).substr($font,6+$info['length1']+6,$info['length2']);
$this->_put('<</Length '.strlen($font));
if($compressed)
$this->_put('/Filter /FlateDecode');
$this->_put('/Length1 '.$info['length1']);
if(isset($info['length2']))
$this->_put('/Length2 '.$info['length2'].' /Length3 0');
$this->_put('>>');
$this->_putstream($font);
$this->_put('endobj');
}
foreach($this->fonts as $k=>$font)
{
// Encoding
if(isset($font['diff']))
{
if(!isset($this->encodings[$font['enc']]))
{
$this->_newobj();
$this->_put('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$font['diff'].']>>');
$this->_put('endobj');
$this->encodings[$font['enc']] = $this->n;
}
}
// ToUnicode CMap
if(isset($font['uv']))
{
if(isset($font['enc']))
$cmapkey = $font['enc'];
else
$cmapkey = $font['name'];
if(!isset($this->cmaps[$cmapkey]))
{
$cmap = $this->_tounicodecmap($font['uv']);
$this->_putstreamobject($cmap);
$this->cmaps[$cmapkey] = $this->n;
}
}
// Font object
$this->fonts[$k]['n'] = $this->n+1;
$type = $font['type'];
$name = $font['name'];
if($font['subsetted'])
$name = 'AAAAAA+'.$name;
if($type=='Core')
{
// Core font
$this->_newobj();
$this->_put('<</Type /Font');
$this->_put('/BaseFont /'.$name);
$this->_put('/Subtype /Type1');
if($name!='Symbol' && $name!='ZapfDingbats')
$this->_put('/Encoding /WinAnsiEncoding');
if(isset($font['uv']))
$this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R');
$this->_put('>>');
$this->_put('endobj');
}
elseif($type=='Type1' || $type=='TrueType')
{
// Additional Type1 or TrueType/OpenType font
$this->_newobj();
$this->_put('<</Type /Font');
$this->_put('/BaseFont /'.$name);
$this->_put('/Subtype /'.$type);
$this->_put('/FirstChar 32 /LastChar 255');
$this->_put('/Widths '.($this->n+1).' 0 R');
$this->_put('/FontDescriptor '.($this->n+2).' 0 R');
if(isset($font['diff']))
$this->_put('/Encoding '.$this->encodings[$font['enc']].' 0 R');
else
$this->_put('/Encoding /WinAnsiEncoding');
if(isset($font['uv']))
$this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R');
$this->_put('>>');
$this->_put('endobj');
// Widths
$this->_newobj();
$cw = $font['cw'];
$s = '[';
for($i=32;$i<=255;$i++)
$s .= $cw[chr($i)].' ';
$this->_put($s.']');
$this->_put('endobj');
// Descriptor
$this->_newobj();
$s = '<</Type /FontDescriptor /FontName /'.$name;
foreach($font['desc'] as $k=>$v)
$s .= ' /'.$k.' '.$v;
if(!empty($font['file']))
$s .= ' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
$this->_put($s.'>>');
$this->_put('endobj');
}
else
{
// Allow for additional types
$mtd = '_put'.strtolower($type);
if(!method_exists($this,$mtd))
$this->Error('Unsupported font type: '.$type);
$this->$mtd($font);
}
}
}

protected function _tounicodecmap($uv)
{
$ranges = '';
$nbr = 0;
$chars = '';
$nbc = 0;
foreach($uv as $c=>$v)
{
if(is_array($v))
{
$ranges .= sprintf("<%02X> <%02X> <%04X>\n",$c,$c+$v[1]-1,$v[0]);
$nbr++;
}
else
{
$chars .= sprintf("<%02X> <%04X>\n",$c,$v);
$nbc++;
}
}
$s = "/CIDInit /ProcSet findresource begin\n";
$s .= "12 dict begin\n";
$s .= "begincmap\n";
$s .= "/CIDSystemInfo\n";
$s .= "<</Registry (Adobe)\n";
$s .= "/Ordering (UCS)\n";
$s .= "/Supplement 0\n";
$s .= ">> def\n";
$s .= "/CMapName /Adobe-Identity-UCS def\n";
$s .= "/CMapType 2 def\n";
$s .= "1 begincodespacerange\n";
$s .= "<00> <FF>\n";
$s .= "endcodespacerange\n";
if($nbr>0)
{
$s .= "$nbr beginbfrange\n";
$s .= $ranges;
$s .= "endbfrange\n";
}
if($nbc>0)
{
$s .= "$nbc beginbfchar\n";
$s .= $chars;
$s .= "endbfchar\n";
}
$s .= "endcmap\n";
$s .= "CMapName currentdict /CMap defineresource pop\n";
$s .= "end\n";
$s .= "end";
return $s;
}

protected function _putimages()
{
foreach(array_keys($this->images) as $file)
{
$this->_putimage($this->images[$file]);
unset($this->images[$file]['data']);
unset($this->images[$file]['smask']);
}
}

protected function _putimage(&$info)
{
$this->_newobj();
$info['n'] = $this->n;
$this->_put('<</Type /XObject');
$this->_put('/Subtype /Image');
$this->_put('/Width '.$info['w']);
$this->_put('/Height '.$info['h']);
if($info['cs']=='Indexed')
$this->_put('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]');
else
{
$this->_put('/ColorSpace /'.$info['cs']);
if($info['cs']=='DeviceCMYK')
$this->_put('/Decode [1 0 1 0 1 0 1 0]');
}
$this->_put('/BitsPerComponent '.$info['bpc']);
if(isset($info['f']))
$this->_put('/Filter /'.$info['f']);
if(isset($info['dp']))
$this->_put('/DecodeParms <<'.$info['dp'].'>>');
if(isset($info['trns']) && is_array($info['trns']))
{
$trns = '';
for($i=0;$i<count($info['trns']);$i++)
$trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
$this->_put('/Mask ['.$trns.']');
}
if(isset($info['smask']))
$this->_put('/SMask '.($this->n+1).' 0 R');
$this->_put('/Length '.strlen($info['data']).'>>');
$this->_putstream($info['data']);
$this->_put('endobj');
// Soft mask
if(isset($info['smask']))
{
$dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$info['w'];
$smask = array('w'=>$info['w'], 'h'=>$info['h'], 'cs'=>'DeviceGray', 'bpc'=>8, 'f'=>$info['f'], 'dp'=>$dp, 'data'=>$info['smask']);
$this->_putimage($smask);
}
// Palette
if($info['cs']=='Indexed')
$this->_putstreamobject($info['pal']);
}

protected function _putxobjectdict()
{
foreach($this->images as $image)
$this->_put('/I'.$image['i'].' '.$image['n'].' 0 R');
}

protected function _putresourcedict()
{
$this->_put('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
$this->_put('/Font <<');
foreach($this->fonts as $font)
$this->_put('/F'.$font['i'].' '.$font['n'].' 0 R');
$this->_put('>>');
$this->_put('/XObject <<');
$this->_putxobjectdict();
$this->_put('>>');
}

protected function _putresources()
{
$this->_putfonts();
$this->_putimages();
// Resource dictionary
$this->_newobj(2);
$this->_put('<<');
$this->_putresourcedict();
$this->_put('>>');
$this->_put('endobj');
}

protected function _putinfo()
{
$date = @date('YmdHisO',$this->CreationDate);
$this->metadata['CreationDate'] = 'D:'.substr($date,0,-2)."'".substr($date,-2)."'";
foreach($this->metadata as $key=>$value)
$this->_put('/'.$key.' '.$this->_textstring($value));
}

protected function _putcatalog()
{
$n = $this->PageInfo[1]['n'];
$this->_put('/Type /Catalog');
$this->_put('/Pages 1 0 R');
if($this->ZoomMode=='fullpage')
$this->_put('/OpenAction ['.$n.' 0 R /Fit]');
elseif($this->ZoomMode=='fullwidth')
$this->_put('/OpenAction ['.$n.' 0 R /FitH null]');
elseif($this->ZoomMode=='real')
$this->_put('/OpenAction ['.$n.' 0 R /XYZ null null 1]');
elseif(!is_string($this->ZoomMode))
$this->_put('/OpenAction ['.$n.' 0 R /XYZ null null '.sprintf('%.2F',$this->ZoomMode/100).']');
if($this->LayoutMode=='single')
$this->_put('/PageLayout /SinglePage');
elseif($this->LayoutMode=='continuous')
$this->_put('/PageLayout /OneColumn');
elseif($this->LayoutMode=='two')
$this->_put('/PageLayout /TwoColumnLeft');
}

protected function _putheader()
{
$this->_put('%PDF-'.$this->PDFVersion);
}

protected function _puttrailer()
{
$this->_put('/Size '.($this->n+1));
$this->_put('/Root '.$this->n.' 0 R');
$this->_put('/Info '.($this->n-1).' 0 R');
}

protected function _enddoc()
{
$this->CreationDate = time();
$this->_putheader();
$this->_putpages();
$this->_putresources();
// Info
$this->_newobj();
$this->_put('<<');
$this->_putinfo();
$this->_put('>>');
$this->_put('endobj');
// Catalog
$this->_newobj();
$this->_put('<<');
$this->_putcatalog();
$this->_put('>>');
$this->_put('endobj');
// Cross-ref
$offset = $this->_getoffset();
$this->_put('xref');
$this->_put('0 '.($this->n+1));
$this->_put('0000000000 65535 f ');
for($i=1;$i<=$this->n;$i++)
$this->_put(sprintf('%010d 00000 n ',$this->offsets[$i]));
// Trailer
$this->_put('trailer');
$this->_put('<<');
$this->_puttrailer();
$this->_put('>>');
$this->_put('startxref');
$this->_put($offset);
$this->_put('%%EOF');
$this->state = 3;
}
}
?>


 

No comments:

Post a Comment

Pertemuan 13 | Abstract Class

 Nama     : Naufal Daffa Alfa Zain  Nrp         : 5025241066  Kelas      : Pemrograman Web A2 Pada pertemuan ke‑13 kami mendapat dua tugas. ...