Lập trình PHP kết hợp JS cho xem file PDF dưới dạng hình ảnh mà không bị lộ đường dẫn file PDF

php and js

Hôm nay, mình sẽ giới thiệu với các bạn một cách kết hợp PHP và JS để viết một trang Web xem nội dung file PDF dưới dạng hình ảnh, mà người truy cập không thể tải file PDF về máy tính. Cách này mình lấy ý tưởng từ PDF JS và mình đã kết hợp PHP vào để thực hiện trong một dự án gần đây.

Trước tiên bạn cần đưa thư viện PDF JS vào thẻ <head> của trang và đoạn CSS định dạng thêm đường viền cho hình nội dung cho dễ nhìn.

<script src="//mozilla.github.io/pdf.js/build/pdf.js"></script>
<style type="text/css">
	#the-canvas {
		border: 1px solid black;
		direction: ltr;
	}
</style>

Tiếp theo bạn xây dựng một bố cục phân trang và đánh số trang xem. Trong đoạn code sau tôi có dùng Class trong thư viện Bootstrap.

<div style="padding: 5px 10px 10px 10px;">
	<button class="btn btn-primary btn-rounded" id="prev">Trang trước</button>
	<button class="btn btn-primary btn-rounded" id="next">Trang sau</button>
	&nbsp; &nbsp;
	<span>Trang: <input id="page_num" value="" onchange="onOfPage(this);" style="width: 40px; text-align: right;"/> / <span id="page_count"></span></span>
</div>
<canvas id="the-canvas"></canvas>

Sau cùng bạn thêm đoạn code sau, đoạn code dùng PHP đọc nội dung file đưa vào JS để hiển thị vào thẻ <canvas> và phân trang.

<?php
	//Khai báo biến lấy nội dung file và encode base64
	$getPDF = base64_encode(file_get_contents('example.pdf'));
?>
<script type="text/javascript">
	var pdfData = atob('<?php echo $getPDF; ?>');
	
	var pdfjsLib = window['pdfjs-dist/build/pdf'];
	
	pdfjsLib.GlobalWorkerOptions.workerSrc = "//mozilla.github.io/pdf.js/build/pdf.worker.js";

	var pdfDoc = null,
		pageNum = 1,
		pageRendering = false,
		pageNumPending = null,
		scale = 1.2,
		canvas = document.getElementById('the-canvas'),
		ctx = canvas.getContext('2d');
	canvas.oncontextmenu = function() {return false};
	var loadingTask = pdfjsLib.getDocument({data: pdfData});
	loadingTask.promise.then(function(pdf) {
		pdfDoc = pdf;
		document.getElementById('page_count').textContent = pdf.numPages;
		renderPage(pageNum);
	}, function (reason) {
		console.error(reason);
	});
	
	function renderPage(num) {
		pageRendering = true;
		pdfDoc.getPage(num).then(function(page) {
			var viewport = page.getViewport({scale: scale});
			canvas.height = viewport.height;
			canvas.width = viewport.width;
			var renderContext = {
				canvasContext: ctx,
				viewport: viewport
			};
			var renderTask = page.render(renderContext);
			renderTask.promise.then(function() {
				pageRendering = false;
				if (pageNumPending !== null) {
					renderPage(pageNumPending);
					pageNumPending = null;
				}
			});
		});
		document.getElementById('page_num').value = num;
	}
	
	function queueRenderPage(num) {
		if (pageRendering)
			pageNumPending = num;
		else
			renderPage(num);
	}
	
	function onPrevPage() {
		if(pageNum <= 1)
			return;
		pageNum--;
		queueRenderPage(pageNum);
	}
	document.getElementById('prev').addEventListener('click', onPrevPage);

	function onNextPage() {
		if (pageNum >= pdfDoc.numPages)
			return;
		pageNum++;
		queueRenderPage(pageNum);
	}
	document.getElementById('next').addEventListener('click', onNextPage);

	function onOfPage(e) {
		var num = parseInt(e.value);
		if(Number.isInteger(num) == false)
			return;
		if(num > pdfDoc.numPages || num < 1)
			return;
		pageNum = num;
		queueRenderPage(pageNum);
	}
</script>

Tới đây trang của bạn có thể hiển thị nội dung của file PDF dưới dạng hình ảnh rồi.

Ngoài ra, bạn cũng có thể thêm đoạn code oncontextmenu="return false;" vào thẻ <body> để chặn người xem tải nội dung file hình về máy tính.

Bạn nào cảm thấy bài viết này hay thì hãy chia sẻ cho những bạn khác cần đến nhé. Chúc các bạn thành công!

Ngụy Kim Hưng

Lập trình php tạo Transparent Watermark Logo (hình logo mờ trong suốt) cho hình ảnh

Chào các bạn, hôm nay mình sẽ giới thiệu cho các bạn đang lập trình php nhưng chưa biết cách tạo hình logo mờ ẩn trên hình (Transparent Watermark Logo).

Đoạn code php sau sẽ giúp các bạn tạo một logo mờ ẩn trên hình. Bạn có thể ứng dụng cho việc upload hình ảnh lên website và gắn logo bản quyền cho hình ảnh đó. Đoạn code chỉ lấy ví dụ hình ảnh có type là image/jpeg nên nếu các bạn muốn thực hiện với type khác bạn hãy tìm hiểu thêm nhé.

// Cấu hình vị trí độ mờ
$horiz_position = 'left'; // center | left | right
$horiz_shift = '20'; //Khoảng cách pixel với tọa độ x
$vert_position = 'top'; // middle | top | bottom
$vert_shift = '20'; //Khoảng cách pixel với tọa độ y
$transparency = '50'; //Tính trong suốt của hình logo mờ
$picture = imagecreatefromjpeg('iot-agitech.jpg'); // Hình ảnh cần gắn logo mờ
$logo_watermark = imagecreatefrompng('logo-cnxag.png'); // Hình logo (chọn file png)

$pct = $transparency/100;
$w = imagesx($logo_watermark);
$h = imagesy($logo_watermark);

imageAlphaBlending($logo_watermark, false);
// Tìm pixel mờ nhất trong hình ảnh (pixel có giá trị alpha nhỏ nhất)
$minAlpha = 127;
for($x = 0; $x < $w; $x++) {
	for($y = 0; $y < $h; $y++) {
		$alpha = (imagecolorat($logo_watermark, $x, $y) >> 24) & 0xFF;
		if($alpha < $minAlpha) {
			$minAlpha = $alpha;
		}
	}
}

// Lặp qua các pixel hình ảnh và sửa đổi alpha cho từng pixel
for($x = 0; $x < $w; $x++) {
	for($y = 0; $y < $h; $y++) {
		$colorXY = imagecolorat($logo_watermark, $x, $y);
		$alpha = ($colorXY >> 24) & 0xFF;
		if($minAlpha !== 127) {
			$alpha = 127 + 127 * $pct * ($alpha - 127) / (127 - $minAlpha);
		} else {
			$alpha += 127 * $pct;
		}
		$alphaColorXY = imagecolorallocatealpha(
			$logo_watermark,
			($colorXY >> 16) & 0xFF,
			($colorXY >> 8) & 0xFF,
			$colorXY & 0xFF,
			$alpha
		);
		if(!imagesetpixel($logo_watermark, $x, $y, $alphaColorXY)) {
			return false;
		}
	}
}

$picture_width=imageSX($picture);
$picture_height=imageSY($picture);
$watermarkfile_width=imageSX($logo_watermark);
$watermarkfile_height=imageSY($logo_watermark);

// Lấy vị trí tọa độ x cho hình mờ
switch ($horiz_position) {
case 'center':
	$dest_x = ( $picture_width / 2 ) - ( $watermarkfile_width / 2 );
	break;
case 'left':
	$dest_x = $horiz_shift;
	break;
case 'right':
	$dest_x = $picture_width - $watermarkfile_width - $horiz_shift;
	break;
}

// Lấy vị trí tọa độ y cho hình mờ
switch ($vert_position) {
case 'middle':
	$dest_y = ( $picture_height / 2 ) - ( $watermarkfile_height / 2 );
	break;
case 'top':
	$dest_y = $vert_shift;
	break;
case 'bottom':
	$dest_y = $picture_height - $watermarkfile_height - $vert_shift;
	break;
}

// Dùng cho hình gif
// if($picture_fileType == 'gif') {
	// $tempimage = imagecreatetruecolor($picture_width, $picture_height);
	// imagecopy($tempimage, $picture, 0, 0, 0, 0, $picture_width, $picture_height);
	// $picture = $tempimage;
// }

imagecopy($picture, $logo_watermark, $dest_x, $dest_y, 0, 0, $watermarkfile_width, $watermarkfile_height);

header('Content-Type: image/png'); 
imagepng($picture);

Kết quả hình ảnh sẽ giống như sau:

tao logo mo an tren hinh

 

Mong là bài viết này sẽ giúp ít cho những bạn đang tìm hiểu và phát triển kỹ năng lập trình php, để viết ra chức năng ứng dụng hay hơn cho trang web của mình.

Chúc các bạn thành công!

Ngụy Kim Hưng

Hướng dẫn viết hàm tạo Captcha trong Laravel Framework

 captcha

Hôm nay mình xin giới thiệu một hàm PHP tự code dùng để tạo Captcha Image sử dụng trong Laravel Framework.

Trước tiên các bạn cần kiểm tra xem có bật sử dụng Extension GD2 trong php chưa, vì hàm có sử dụng thư viện để tạo Image.

Các bạn tạo một Route đến Controller chứa hàm getCaptcha bên dưới.

public function getCaptcha(Request $request)
{
	$font = public_path('fonts/arial.ttf'); //đường dẫn font chữ trong public
	$name = $request->name?$request->name:'captchacode'; //lấy tên session từ request, mặc định là captchacode
	$lenght = $request->lenght?$request->lenght:'5'; //số ký tự lấy từ request, mặc định là 5
	$width = $request->width?$request->width:'160'; //chiều rộng hình lấy từ request, mặc định là 160
	$height = $request->height?$request->height:'40'; //chiều cao hình lấy từ request, mặc định là 40
	$code = $this->generateCode($lenght); //Hàm lấy ngẫu nhiên theo số ký tự
	$font_size = $height * 0.75;
	$image = imagecreate($width, $height) or die('Cannot initialize new GD image stream');
	$background_color = imagecolorallocate($image, 255, 255, 255);
	$text_color = imagecolorallocate($image, 20, 40, 100);
	$noise_color = imagecolorallocate($image, 255, 255, 255);
	for( $i=0; $i<($width*$height)/3; $i++ ) {
		imagefilledellipse($image, mt_rand(0,$width), mt_rand(0,$height), 1, 1, $noise_color);
	}
	for( $i=0; $i<($width*$height)/150; $i++ ) {
		imageline($image, mt_rand(0,$width), mt_rand(0,$height), mt_rand(0,$width), mt_rand(0,$height), $noise_color);
	}
	$textbox = imagettfbbox($font_size, 0, $font, $code);
	$x = ($width - $textbox[4])/2;
	$y = ($height - $textbox[5])/2;
	imagettftext($image, $font_size, 0, $x, $y, $text_color, $font , $code);
	ob_start();
	header('Content-Type: image/jpeg');
	imagejpeg($image);
	imagedestroy($image);
	$image_data = ob_get_contents();
	ob_end_clean();
	Session::put($name, $code); //Lưu session mã captcha để kiểm tra khi submit
	return Response::make($image_data, 200, ['Content-Type' => 'image/jpeg']);
}

public function GenerateCode($str)
{
	$possible = 'ABCDEFGHJKLMNPRSTUVWXYZ23456789'; //Danh sách các chữ cái được lấy ngẫu nhiên
	$code = '';
	$i = 0;
	while ($i < $str) { 
		$code .= substr($possible, mt_rand(0, strlen($possible)-1), 1);
		$i++;
	}
	return $code;
}

Tại view các bạn chỉ cần gắn Route trong src của thẻ img sẽ có hình ảnh captcha.

Khi submit form các bạn chỉ cần kiểm tra giá trị trường nhập mã captcha với Session::get('captchacode') có trùng khớp với nhau hay không.

Về phần lập trình tạo form và kiểm tra mình không mô tả chi tiết lên đây, để các bạn độc giả tự viết theo ý của các bạn.

Chúc các bạn thành công!

Chia sẻ: Ngụy Kim Hưng

Cách tắt báo lỗi và thực hiện lấy lại dữ liệu với Ajax DataTable

datatable ajax

1. Giới thiệu:

Sau khi tham gia một số dự án web sử dụng thư viện JavaScript DataTable mình đã biết đến Ajax DataTable với những chức năng tiện lợi cho một dữ liệu không quá lớn.

Ưu điểm: Thư viện giúp lấy dữ liệu nhanh, tự phân trang, tìm kiếm cơ bản mà không cần phải reload lại trang web khi sử dụng Ajax DataTable.

Khuyết điểm: JavaScript Datatable có một khuyết điểm là tải dữ liệu chậm nếu dữ liệu quá lớn vì nó không thật sự lấy theo số dòng dữ liệu mà bạn đặt ra. Bạn phải đưa toàn bộ bảng dữ liệu cho nó, nó sẽ tự phân trang lại theo cấu hình của bạn.

Trong bài viết này mình sẽ hướng dẫn các bạn sử dụng Ajax DataTable và một số thủ thuật nhỏ như tắt báo lỗi khi lấy dữ liệu không thành công hay tự động lấy lại dữ liệu khi lấy dữ liệu thất bại.

2. Thủ thuật:

Ẩn thông báo lỗi: Để ẩn thông báo lỗi do lấy dữ liệu không được hoặc trong quá trình đỗ dữ liệu vào bị lỗi. Bạn chỉ cần thêm đoạn code sau vào phía trước hàm cấu hình Datatable.

$.fn.dataTable.ext.errMode = 'none';

Dưới đây là đoạn code sử dụng Ajax DataTabletự lấy lại dữ liệu nếu thất bại.

Trong đoạn code tôi có thêm đếm số lần lấy lại và giới hạn không quá 10 lần. Nếu trường hợp bạn đang kiểm thử có lỗi PHP thì sẽ ảnh hưởng tới việc Server tự ghi log lỗi PHP lại với dung lượng lớn vì hàm sẽ gọi lấy dữ liệu liên tục, điều này sẽ phiền bạn tìm kiếm file log để xóa nó đi.

Nếu bạn đang sử dụng Laravel Framework thì cần thêm csrf-token. Xem chi tiết tại đây.

<script type="text/javascript">	
	var countError = 0; //Tạo biến giới hạn reload ajax khi bị lỗi
	var DataList = $("#DataList").DataTable({
		"aLengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, "Tất cả"]], //Cấu hình số dòng lấy
		"iDisplayLength": 10, //Số dòng mặc định
		"processing": true, //Cấu hình hiển thị chờ lấy dữ liệu
		"searching": false, //Cấu hình hiển thị tìm kiếm
		"ajax": {
			"url": "<Đường dẫn lấy dữ liệu của bạn>",
			"type": "<Phương thức gửi>", //POST or GET
			"data": data, //Mảng dữ liệu truyền vào nếu có
			//Nếu lấy lỗi sẽ thực hiện lấy lại dữ liệu và không quá 10 lần				
			"error": function (){
				if(countError<9)
					DataList.ajax.reload( null, false );
				countError++;
			}
		},
		//Đỗ dữ liệu vào
		"columns": [
			{data: "<Tên trường dữ liệu>"},
			...
		],
		//Cấu hình cho ngôn ngữ tiếng việt với Bootstrap icon
		"language": {
			"sLengthMenu": "Hiển thị _MENU_ dòng mỗi trang",
			"oPaginate": {
				"sFirst": "",
				"sLast": "",
				"sNext": "",
				"sPrevious": ""
			},
			"sEmptyTable": "Không có dữ liệu",
			"sSearch": "Tìm kiếm:",
			"sZeroRecords": "Không có dữ liệu",
			"sInfo": "Hiển thị từ _START_ đến _END_ trong tổng số _TOTAL_ dòng được tìm thấy",
			"sInfoEmpty" : "Không tìm thấy",
			"sInfoFiltered": " (trong tổng số _MAX_ dòng)",
			"processing": "Đang tải dữ liệu!"
		},
		//Tạo tên class cho hoặc sẽ không sắp xếp cột
		"columnDefs": [ {
			"targets"  : 'no-sort',
			"orderable": false,
		}],
	});
</script>	

Cảm ơn các bạn đã đọc bài viết của mình. Hy vọng bài viết của mình có thể giúp ích cho các bạn đang dùng JavaScript DataTable.

Chúc các bạn thành công!

Chia sẻ: Ngụy Kim Hưng

Chia sẻ cách viết Ajax trong Laravel Framework

laravel ajax

Mình đang tham gia một dự án phần mềm viết bằng Laravel Framework. Trong quá trình tìm hiểu và thực hiện một chức năng cần thiết phải sử dụng JavaScript Ajax để không cần load lại trang để lấy dữ liệu hiển thị. Hôm nay mình sẽ chia sẻ cho các bạn một cách tạo một Ajax lấy dữ liệu trong Laravel Framework mong là sẽ giúp ích cho những bạn đã đọc bài chia sẻ này của mình.

Như các bạn đã biết Laravel Framework luôn yêu cầu một _token đối với URL phương thức POST vì thế việc cần làm trước tiên để Ajax của bạn có thể chạy với phương thức POST và gửi Request là khai báo cho một token. Các bạn thực hiện:

- Thêm thẻ <meta> vào <head> như sau: 

 <meta name="csrf-token" content="{{ csrf_token() }}">

- Tiếp theo trước đoạn code Ajax bạn thêm đoạn code Setup để gửi token trong Ajax:

$.ajaxSetup({
	headers: {
	'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
	}
});
$.ajax({
	type: "POST",
	cache: false,
	url: "URL_POST",
	data: {DATA_REQUEST},
	dataType: "json",
	success: function(data){
	//Code của bạn
	},
	error: function(error){
	//Code của bạn
	}
});

Đây là cách để một Ajax chạy trong laravel. Những bài sau mình sẽ hướng dẫn cách xử lý dữ liệu data kiểu json trả về cho những bạn nào chưa biết.

Chúc các bạn thành công!

Chia sẻ: Ngụy Kim Hưng