Selasa, 25 Januari 2011

Tentang Epson T13



Mungkin anda sekalian tau printer diatas..
Udah pasti printer ini adalah printer epson T13. Printer pertama yang saya beli pada akhir desember 2010.

Menjelang skripsi, saya mulai dipusingkan dengan yg namanya draft. Sedikit-dikit ada yg harus diprint. Dan saya mulai memilih-milih printer mana yg sesuai dengan uang saku mahasiswa macam saya. Saya cari deh printer-printer dengan range 400rb-1jt.

Asalnya saya memilih printer HP d1660 , dengan alasan produk-produk keluaran HP cukup bagus dan bodinya yahud. Setelah nanya ke tukang infus, ternyata printer HP susah diinfus dan tintanya wajib ori.
Kalo dikasih tinta non-ori biasanya printer suka ngadat. Ga mau ngeprint. Alhasil, walaupun harga printernya cuman 300rban(tanpa infus) dan catridgenya lebih murah dari canon, saya berpikir 2x mengingat biaya operasionalnya cukup tinggi.

Lalu saya beralih ke canon ip2770, salah satu printer paling branded, dengan harga printer paling murah sekitar 400rban(tanpa infus), saya tertarik dengan tampilan bodinya yg yahud dan keunggulan utamanya di sektor kecepatan (dibandingkan epson). Tintanya juga bisa non ori, dan hemat dari biaya operasional tinta. Yg saya jadikan alasan tidak memilih ini adalah karena cartridgenya yg mahal. 2 catridge (warna+hitam) harganya nyaris seharga printernya. Dan kebayang, kalo saya harus suntik cartridge dan terjadi kecelakaan dalam suntik.
Bisa-bisa catridge melayang a.k.a rusak, saya kemudian berpikir untuk menginfus printer ini, tapi menurut teman saya(yg sudah memakai canon+infus) hasilnya kurang bagus, suka ngadat. Kalo botol tinta lebih tinggi dari printer, si cartridge suka banjir.
Saya berpikir tentang printer ini, untuk skripsi terlihat ribet amat ya.. Bisa-bisa kalo ngeprint mendadak malah timbul masalah ini-itu di printernya.

Akhirnya saya memilih printer ini, walaupun saya tau printer ini agak lambat kalo print.
Waktu pertama, saya berpikir untuk tidak menginfusnya. Tapi karena cartridgenya cuman sekali pakai,saya ga mau harus beli cartridge lagi. Walaupun cartridgenya murah bgt, seriusan!
Pertimbangan lainnya, saya membeli printer ini sebenarnya ada pada kelebihannya yg mudah udah dinfus dan cukup bandel. Lumayan loh, biaya operasional sekali print kertas jauh lebih murah daripada printer-printer diatas, walaupun saya harus merogoh kocek lebih dalam untuk membeli printer(+infusnya) ini.
Sudah 1.5 tahun saya pakai, sejauh ini tidak pernah ada masalah yang rumit. Setlah saya pakai, kelebihan dan kekurangan printer ini adalah sebagai berikut.

Kelebihan printer ini :
1. Mudah diinfus, sehingga biayanya ngeprint jauh lebih murah.
2. Mesin bandel
3. Bodinya tebal dan mudah mencabut cartridgenya
4. Printhead pisah dengan cartridgenya, sehingga kerusakan printhead tidak berefek pada komponen lain.
5. Ngeprint gambar bagus dan cukup detail
6. Jarang paper-jam.
7. Perawatan terhitung mudah.

Kekurangan :
1. lambat, ngeprint 20 lembar butuh waktu sekitar 15 menitan itupun udah pake fast mode.
2. bodinya gede
3. Agak mahal ketimbang printer dengan spesifikasi sejenis
4. Agak boros tinta, tapi ga masaklah karena sudah infus
5. Setelah beberapa ngeprint beberapa puluh kertas, printer ini suka minta direset.
6. tempat nyimpen kertasnya sempit, cuman bisa nampung kertas <100 lembar.
7. Jika printer dibiarkan terlalu lama, tinta di-print head kadang suka mengering.





Senin, 17 Januari 2011

Simple Multiple Upload pada Codeigniter

Sederhananya, codeigniter memiliki library sendiri untuk melakukan upload.
Hanya saja, kemampuan codeigniter untuk melakukan multiple upload(upload dengan banyak file sekaligus) sangat terbatas dan hanya mampu melakukan upload 1 file saja.. Sehingga disini saya melakukan modifikasi agar upload bisa dilakukan menggunakan banyak file.

Ini view yg saya buat. Kita beri nama view ini , view_form.php

function addInput()
{
var br=document.createElement("br");
var inputfiles = document.createElement("input");
inputfiles.type='file';
inputfiles.name='file[]';
inputfiles.size=20;
var inputnama = document.createElement("input");
inputnama.type='text';
inputnama.name='nama[]';
var inputdeskripsi = document.createElement("input");
inputdeskripsi.type='text';
inputdeskripsi.name='deskripsi[]';

document.getElementById("upload").appendChild(document.createElement("br"));
document.getElementById("upload").appendChild(inputfiles);
document.getElementById("upload").appendChild(document.createElement("br"));
document.getElementById("upload").appendChild(inputnama);
document.getElementById("upload").appendChild(document.createElement("br"));
document.getElementById("upload").appendChild(inputdeskripsi);
}

<form action='galeri/add_foto' enctype='multipart/form-data' method='POST'>

< input name='file[]' type='file'>
< input name='nama[]' type='text'>
< input name='deskripsi[]' type='text' >
< input type='submit' value='submit'>
</form >
lalu pada controller galeri, pada function add_foto(), ini skripnya..
.
.
.
function add_foto() {
$arrNama=$_POST['nama'];
$arrDesc=$_POST['deskripsi'];
$arrThumb=array();
$arrFoto=array();

unset($_POST['nama']);
unset($_POST['deskripsi']);

$config['upload_path'] = './uploads/';
$config['allowed_types'] = 'jpg|jpeg|png';
$config['max_size'] = '3072';
$config['remove_spaces']= TRUE;
$config['encrypt_name'] = TRUE;

$this->load->library('multiple_upload');
$CI =& get_instance();
$files = array();
$i=0;

foreach($_FILES as $key => $value) {

$this->multiple_upload->initialize($config);

while($i
$this->load->library('image_lib');

if( ! empty($value['name'][$i])) {
if( ! $this->multiple_upload->do_upload($key,$i)) {
echo $this->multiple_upload->display_errors();
$errors = TRUE;
}
else {
// Build a file array from all uploaded files
$files[] = $CI->multiple_upload->data();
}
}
i++;
}
}
}
.
.
.

Disini saya membuat library baru, untuk melakukan multiple upload, saya beri nama librarinya Multiple_Upload, taruh di library folder pada projek CI anda..

.
.
.
if (!defined('BASEPATH'))
exit('No direct script access allowed');

/*
* librari untuk multiple upload
*/


class Multiple_Upload {

var $max_size = 0;
var $max_width = 0;
var $max_height = 0;
var $max_filename = 0;
var $allowed_types = "";
var $file_temp = "";
var $file_name = "";
var $orig_name = "";
var $file_type = "";
var $file_size = "";
var $file_ext = "";
var $upload_path = "";
var $overwrite = FALSE;
var $encrypt_name = FALSE;
var $is_image = FALSE;
var $image_width = '';
var $image_height = '';
var $image_type = '';
var $image_size_str = '';
var $error_msg = array();
var $mimes = array();
var $remove_spaces = TRUE;
var $xss_clean = FALSE;
var $temp_prefix = "temp_file_";

/**
* Constructor
*
* @access public
*/
function Multiple_Upload($props = array())
{
if (count($props) > 0)
{
$this->initialize($props);
}

log_message('debug', "Upload Class Initialized");
}

// --------------------------------------------------------------------

/**
* Initialize preferences
*
* @access public
* @param array
* @return void
*/
function initialize($config = array())
{
$defaults = array(
'max_size' => 0,
'max_width' => 0,
'max_height' => 0,
'max_filename' => 0,
'allowed_types' => "",
'file_temp' => "",
'file_name' => "",
'orig_name' => "",
'file_type' => "",
'file_size' => "",
'file_ext' => "",
'upload_path' => "",
'overwrite' => FALSE,
'encrypt_name' => FALSE,
'is_image' => FALSE,
'image_width' => '',
'image_height' => '',
'image_type' => '',
'image_size_str' => '',
'error_msg' => array(),
'mimes' => array(),
'remove_spaces' => TRUE,
'xss_clean' => FALSE,
'temp_prefix' => "temp_file_"
);


foreach ($defaults as $key => $val)
{
if (isset($config[$key]))
{
$method = 'set_'.$key;
if (method_exists($this, $method))
{
$this->$method($config[$key]);
}
else
{
$this->$key = $config[$key];
}
}
else
{
$this->$key = $val;
}
}
}

// --------------------------------------------------------------------

/**
* Perform the file upload
*
* @access public
* @return bool
*/
function do_upload($field = 'userfile',$index=0)
{
// Is $_FILES[$field] set? If not, no reason to continue.
if ( ! isset($_FILES[$field]))
{
$this->set_error('upload_no_file_selected');
return FALSE;
}

// Is the upload path valid?
if ( ! $this->validate_upload_path())
{
// errors will already be set by validate_upload_path() so just return FALSE
return FALSE;
}

// Was the file able to be uploaded? If not, determine the reason why.
if ( ! is_uploaded_file($_FILES[$field]['tmp_name'][$index]))
{
$error = ( ! isset($_FILES[$field]['error'][$index])) ? 4 : $_FILES[$field]['error'][$index];

switch($error)
{
case 1: // UPLOAD_ERR_INI_SIZE
$this->set_error('upload_file_exceeds_limit');
break;
case 2: // UPLOAD_ERR_FORM_SIZE
$this->set_error('upload_file_exceeds_form_limit');
break;
case 3: // UPLOAD_ERR_PARTIAL
$this->set_error('upload_file_partial');
break;
case 4: // UPLOAD_ERR_NO_FILE
$this->set_error('upload_no_file_selected');
break;
case 6: // UPLOAD_ERR_NO_TMP_DIR
$this->set_error('upload_no_temp_directory');
break;
case 7: // UPLOAD_ERR_CANT_WRITE
$this->set_error('upload_unable_to_write_file');
break;
case 8: // UPLOAD_ERR_EXTENSION
$this->set_error('upload_stopped_by_extension');
break;
default : $this->set_error('upload_no_file_selected');
break;
}

return FALSE;
}

// Set the uploaded data as class variables
$this->file_temp = $_FILES[$field]['tmp_name'][$index];
$this->file_name = $this->_prep_filename($_FILES[$field]['name'][$index]);
$this->file_size = $_FILES[$field]['size'][$index];
$this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $_FILES[$field]['type'][$index]);
$this->file_type = strtolower($this->file_type);
$this->file_ext = $this->get_extension($_FILES[$field]['name'][$index]);

// Convert the file size to kilobytes
if ($this->file_size > 0)
{
$this->file_size = round($this->file_size/1024, 2);
}

// Is the file type allowed to be uploaded?
if ( ! $this->is_allowed_filetype())
{
$this->set_error('upload_invalid_filetype');
return FALSE;
}

// Is the file size within the allowed maximum?
if ( ! $this->is_allowed_filesize())
{
$this->set_error('upload_invalid_filesize');
return FALSE;
}

// Are the image dimensions within the allowed size?
// Note: This can fail if the server has an open_basdir restriction.
if ( ! $this->is_allowed_dimensions())
{
$this->set_error('upload_invalid_dimensions');
return FALSE;
}

// Sanitize the file name for security
$this->file_name = $this->clean_file_name($this->file_name);

// Truncate the file name if it's too long
if ($this->max_filename > 0)
{
$this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
}

// Remove white spaces in the name
if ($this->remove_spaces == TRUE)
{
$this->file_name = preg_replace("/\s+/", "_", $this->file_name);
}

/*
* Validate the file name
* This function appends an number onto the end of
* the file if one with the same name already exists.
* If it returns false there was a problem.
*/
$this->orig_name = $this->file_name;

if ($this->overwrite == FALSE)
{
$this->file_name = $this->set_filename($this->upload_path, $this->file_name);

if ($this->file_name === FALSE)
{
return FALSE;
}
}

/*
* Move the file to the final destination
* To deal with different server configurations
* we'll attempt to use copy() first. If that fails
* we'll use move_uploaded_file(). One of the two should
* reliably work in most environments
*/
if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name))
{
if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))
{
$this->set_error('upload_destination_error');
return FALSE;
}
}

/*
* Run the file through the XSS hacking filter
* This helps prevent malicious code from being
* embedded within a file. Scripts can easily
* be disguised as images or other file types.
*/
if ($this->xss_clean == TRUE)
{
$this->do_xss_clean();
}

/*
* Set the finalized image dimensions
* This sets the image width/height (assuming the
* file was an image). We use this information
* in the "data" function.
*/
$this->set_image_properties($this->upload_path.$this->file_name);

return TRUE;
}

// --------------------------------------------------------------------

/**
* Finalized Data Array
*
* Returns an associative array containing all of the information
* related to the upload, allowing the developer easy access in one array.
*
* @access public
* @return array
*/
function data()
{
return array (
'file_name' => $this->file_name,
'file_type' => $this->file_type,
'file_path' => $this->upload_path,
'full_path' => $this->upload_path.$this->file_name,
'raw_name' => str_replace($this->file_ext, '', $this->file_name),
'orig_name' => $this->orig_name,
'file_ext' => $this->file_ext,
'file_size' => $this->file_size,
'is_image' => $this->is_image(),
'image_width' => $this->image_width,
'image_height' => $this->image_height,
'image_type' => $this->image_type,
'image_size_str' => $this->image_size_str,
);
}

// --------------------------------------------------------------------

/**
* Set Upload Path
*
* @access public
* @param string
* @return void
*/
function set_upload_path($path)
{
// Make sure it has a trailing slash
$this->upload_path = rtrim($path, '/').'/';
}

// --------------------------------------------------------------------

/**
* Set the file name
*
* This function takes a filename/path as input and looks for the
* existence of a file with the same name. If found, it will append a
* number to the end of the filename to avoid overwriting a pre-existing file.
*
* @access public
* @param string
* @param string
* @return string
*/
function set_filename($path, $filename)
{
if ($this->encrypt_name == TRUE)
{
mt_srand();
$filename = md5(uniqid(mt_rand())).$this->file_ext;
}

if ( ! file_exists($path.$filename))
{
return $filename;
}

$filename = str_replace($this->file_ext, '', $filename);

$new_filename = '';
for ($i = 1; $i <>
{
if ( ! file_exists($path.$filename.$i.$this->file_ext))
{
$new_filename = $filename.$i.$this->file_ext;
break;
}
}

if ($new_filename == '')
{
$this->set_error('upload_bad_filename');
return FALSE;
}
else
{
return $new_filename;
}
}

// --------------------------------------------------------------------

/**
* Set Maximum File Size
*
* @access public
* @param integer
* @return void
*/
function set_max_filesize($n)
{
$this->max_size = ((int) $n <>
}

// --------------------------------------------------------------------

/**
* Set Maximum File Name Length
*
* @access public
* @param integer
* @return void
*/
function set_max_filename($n)
{
$this->max_filename = ((int) $n <>
}

// --------------------------------------------------------------------

/**
* Set Maximum Image Width
*
* @access public
* @param integer
* @return void
*/
function set_max_width($n)
{
$this->max_width = ((int) $n <>
}

// --------------------------------------------------------------------

/**
* Set Maximum Image Height
*
* @access public
* @param integer
* @return void
*/
function set_max_height($n)
{
$this->max_height = ((int) $n <>
}

// --------------------------------------------------------------------

/**
* Set Allowed File Types
*
* @access public
* @param string
* @return void
*/
function set_allowed_types($types)
{
$this->allowed_types = explode('|', $types);
}

// --------------------------------------------------------------------

/**
* Set Image Properties
*
* Uses GD to determine the width/height/type of image
*
* @access public
* @param string
* @return void
*/
function set_image_properties($path = '')
{
if ( ! $this->is_image())
{
return;
}

if (function_exists('getimagesize'))
{
if (FALSE !== ($D = @getimagesize($path)))
{
$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');

$this->image_width = $D['0'];
$this->image_height = $D['1'];
$this->image_type = ( ! isset($types[$D['2']])) ? 'unknown' : $types[$D['2']];
$this->image_size_str = $D['3']; // string containing height and width
}
}
}

// --------------------------------------------------------------------

/**
* Set XSS Clean
*
* Enables the XSS flag so that the file that was uploaded
* will be run through the XSS filter.
*
* @access public
* @param bool
* @return void
*/
function set_xss_clean($flag = FALSE)
{
$this->xss_clean = ($flag == TRUE) ? TRUE : FALSE;
}

// --------------------------------------------------------------------

/**
* Validate the image
*
* @access public
* @return bool
*/
function is_image()
{
// IE will sometimes return odd mime-types during upload, so here we just standardize all
// jpegs or pngs to the same file type.

$png_mimes = array('image/x-png');
$jpeg_mimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg');

if (in_array($this->file_type, $png_mimes))
{
$this->file_type = 'image/png';
}

if (in_array($this->file_type, $jpeg_mimes))
{
$this->file_type = 'image/jpeg';
}

$img_mimes = array(
'image/gif',
'image/jpeg',
'image/png',
);

return (in_array($this->file_type, $img_mimes, TRUE)) ? TRUE : FALSE;
}

// --------------------------------------------------------------------

/**
* Verify that the filetype is allowed
*
* @access public
* @return bool
*/
function is_allowed_filetype()
{
if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types))
{
$this->set_error('upload_no_file_types');
return FALSE;
}

$image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');

foreach ($this->allowed_types as $val)
{
$mime = $this->mimes_types(strtolower($val));

// Images get some additional checks
if (in_array($val, $image_types))
{
if (getimagesize($this->file_temp) === FALSE)
{
return FALSE;
}
}

if (is_array($mime))
{
if (in_array($this->file_type, $mime, TRUE))
{
return TRUE;
}
}
else
{
if ($mime == $this->file_type)
{
return TRUE;
}
}
}

return FALSE;
}

// --------------------------------------------------------------------

/**
* Verify that the file is within the allowed size
*
* @access public
* @return bool
*/
function is_allowed_filesize()
{
if ($this->max_size != 0 AND $this->file_size > $this->max_size)
{
return FALSE;
}
else
{
return TRUE;
}
}

// --------------------------------------------------------------------

/**
* Verify that the image is within the allowed width/height
*
* @access public
* @return bool
*/
function is_allowed_dimensions()
{
if ( ! $this->is_image())
{
return TRUE;
}

if (function_exists('getimagesize'))
{
$D = @getimagesize($this->file_temp);

if ($this->max_width > 0 AND $D['0'] > $this->max_width)
{
return FALSE;
}

if ($this->max_height > 0 AND $D['1'] > $this->max_height)
{
return FALSE;
}

return TRUE;
}

return TRUE;
}

// --------------------------------------------------------------------

/**
* Validate Upload Path
*
* Verifies that it is a valid upload path with proper permissions.
*
*
* @access public
* @return bool
*/
function validate_upload_path()
{
if ($this->upload_path == '')
{
$this->set_error('upload_no_filepath');
return FALSE;
}

if (function_exists('realpath') AND @realpath($this->upload_path) !== FALSE)
{
$this->upload_path = str_replace("\\", "/", realpath($this->upload_path));
}

if ( ! @is_dir($this->upload_path))
{
$this->set_error('upload_no_filepath');
return FALSE;
}

if ( ! is_really_writable($this->upload_path))
{
$this->set_error('upload_not_writable');
return FALSE;
}

$this->upload_path = preg_replace("/(.+?)\/*$/", "\\1/", $this->upload_path);
return TRUE;
}

// --------------------------------------------------------------------

/**
* Extract the file extension
*
* @access public
* @param string
* @return string
*/
function get_extension($filename)
{
$x = explode('.', $filename);
return '.'.end($x);
}

// --------------------------------------------------------------------

/**
* Clean the file name for security
*
* @access public
* @param string
* @return string
*/
function clean_file_name($filename)
{
$bad = array(
"",
"'",
"<",
">",
'"',
'&',
'$',
'=',
';',
'?',
'/',
"%20",
"%22",
"%3c", // <
"%253c", // <
"%3e", // >
"%0e", // >
"%28", // (
"%29", // )
"%2528", // (
"%26", // &
"%24", // $
"%3f", // ?
"%3b", // ;
"%3d" // =
);

$filename = str_replace($bad, '', $filename);

return stripslashes($filename);
}

// --------------------------------------------------------------------

/**
* Limit the File Name Length
*
* @access public
* @param string
* @return string
*/
function limit_filename_length($filename, $length)
{
if (strlen($filename) < $length)
{
return $filename;
}

$ext = '';
if (strpos($filename, '.') !== FALSE)
{
$parts = explode('.', $filename);
$ext = '.'.array_pop($parts);
$filename = implode('.', $parts);
}

return substr($filename, 0, ($length - strlen($ext))).$ext;
}

// --------------------------------------------------------------------

/**
* Runs the file through the XSS clean function
*
* This prevents people from embedding malicious code in their files.
* I'm not sure that it won't negatively affect certain files in unexpected ways,
* but so far I haven't found that it causes trouble.
*
* @access public
* @return void
*/
function do_xss_clean()
{
$file = $this->upload_path.$this->file_name;

if (filesize($file) == 0)
{
return FALSE;
}

if (($data = @file_get_contents($file)) === FALSE)
{
return FALSE;
}

if ( ! $fp = @fopen($file, FOPEN_READ_WRITE))
{
return FALSE;
}

$CI =& get_instance();
$data = $CI->input->xss_clean($data);

flock($fp, LOCK_EX);
fwrite($fp, $data);
flock($fp, LOCK_UN);
fclose($fp);
}

// --------------------------------------------------------------------

/**
* Set an error message
*
* @access public
* @param string
* @return void
*/
function set_error($msg)
{
$CI =& get_instance();
$CI->lang->load('upload');

if (is_array($msg))
{
foreach ($msg as $val)
{
$msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);
$this->error_msg[] = $msg;
log_message('error', $msg);
}
}
else
{
$msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
$this->error_msg[] = $msg;
log_message('error', $msg);
}
}

// --------------------------------------------------------------------

/**
* Display the error message
*
* @access public
* @param string
* @param string
* @return string
*/
function display_errors($open = '
', $close = '
')
{
$str = '';
foreach ($this->error_msg as $val)
{
$str .= $open.$val.$close;
}

return $str;
}

// --------------------------------------------------------------------

/**
* List of Mime Types
*
* This is a list of mime types. We use it to validate
* the "allowed types" set by the developer
*
* @access public
* @param string
* @return string
*/
function mimes_types($mime)
{
global $mimes;

if (count($this->mimes) == 0)
{
if (@require_once(APPPATH.'config/mimes'.EXT))
{
$this->mimes = $mimes;
unset($mimes);
}
}

return ( ! isset($this->mimes[$mime])) ? FALSE : $this->mimes[$mime];
}

// --------------------------------------------------------------------

/**
* Prep Filename
*
* Prevents possible script execution from Apache's handling of files multiple extensions
* http://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext
*
* @access private
* @param string
* @return string
*/
function _prep_filename($filename)
{
if (strpos($filename, '.') === FALSE)
{
return $filename;
}

$parts = explode('.', $filename);
$ext = array_pop($parts);
$filename = array_shift($parts);

foreach ($parts as $part)
{
if ($this->mimes_types(strtolower($part)) === FALSE)
{
$filename .= '.'.$part.'_';
}
else
{
$filename .= '.'.$part;
}
}

// file name override, since the exact name is provided, no need to
// run it through a $this->mimes check.
if ($this->file_name != '')
{
$filename = $this->file_name;
}

$filename .= '.'.$ext;

return $filename;
}
}

.
.
.

jangan lupa, siapkan direktori 'uploads' pada direktori projek anda, sejajar dengan index.php milik CI... happy coding everyone.. :)

Hilang di Kebun Binatang Bandung | Ketemu pahlawan

Baiklah kali ini saya mau bercerita mengenai pahlawan. Suatu hari aku pernah  bertanya ke temanku namanya si Hermawan, 'What is hero? ...