<?php
//============================================================+
// File name   : tce_pdf_testgen.php
// Begin       : 2004-06-13
// Last Update : 2013-05-31
//
// Description : Creates PDF documents for offline testing.
//
// Author: Nicola Asuni
//
// (c) Copyright:
//               Nicola Asuni
//               Tecnick.com LTD
//               www.tecnick.com
//               info@tecnick.com
//
// License:
//    Copyright (C) 2004-2012  Nicola Asuni - Tecnick.com LTD
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Affero General Public License as
//    published by the Free Software Foundation, either version 3 of the
//    License, or (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU Affero General Public License for more details.
//
//    You should have received a copy of the GNU Affero General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//    Additionally, you can't remove, move or hide the original TCExam logo,
//    copyrights statements and links to Tecnick.com and TCExam websites.
//
//    See LICENSE.TXT file for more information.
//============================================================+

/**
 * @file
 * Creates PDF documents for Pen-and-Paper testing.
 * @package com.tecnick.tcexam.admin
 * @author Nicola Asuni
 * @since 2004-06-13
 * @param $_REQUEST['test_id'] (int) test ID
 * @param $_REQUEST['num'] (int) number of tests to generate
 */

require_once('../config/tce_config.php');
require_once('../../shared/code/tce_authorization.php');
require_once('../../shared/code/tce_functions_tcecode.php');
require_once('../../shared/code/tce_functions_test.php');
require_once('tce_functions_omr.php');
require_once('../../shared/config/tce_pdf.php');
require_once('../../shared/code/tcpdfex.php');

// --- Initialize variables
if (isset($_REQUEST['test_id']) AND ($_REQUEST['test_id'] > 0)) {
	$test_id = intval($_REQUEST['test_id']);
	// check user's authorization
	if (!F_isAuthorizedUser(K_TABLE_TESTS, 'test_id', $test_id, 'test_user_id')) {
		exit;
	}
} else {
	exit;
}

if (isset($_REQUEST['num'])) {
	$test_num = intval($_REQUEST['num']);
} else {
	$test_num = 1;
}

$doc_title = unhtmlentities($l['w_test']);
$doc_description = F_compact_string(unhtmlentities($l['h_test']));
$page_elements = 6;
$qtype = array('S', 'M', 'T', 'O'); // question types

// --- create pdf document

if ($l['a_meta_dir'] == 'rtl') {
	$dirlabel = 'L';
	$dirvalue = 'R';
} else {
	$dirlabel = 'R';
	$dirvalue = 'L';
}

$isunicode = (strcasecmp($l['a_meta_charset'], 'UTF-8') == 0);
//create new PDF document (document units are set by default to millimeters)
$pdf = new TCPDFEX(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, $isunicode);

// set header backlink QR-Code
$header_backlink = K_PATH_URL.'admin/code/tce_edit_test.php?test_id='.$test_id;
$pdf->setTCExamBackLink($header_backlink);

// set document information
$pdf->SetCreator('TC'.'Ex'.'am'.' ver.'.K_TCEXAM_VERSION.'');
$pdf->SetAuthor(PDF_AUTHOR);
$pdf->SetTitle($doc_title);
$pdf->SetSubject($doc_description);
$pdf->SetKeywords('TCExam, '.$doc_title);
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); //set image scale factor

$pdf->setHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE, PDF_HEADER_STRING);

//set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
//set auto page breaks
$pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
$pdf->setHeaderMargin(PDF_MARGIN_HEADER);
$pdf->setFooterMargin(PDF_MARGIN_FOOTER);

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

$pdf->setLanguageArray($l); //set language items

if (defined('K_DIGSIG_ENABLE') AND K_DIGSIG_ENABLE) {
	// set document signature
	$pdf->setSignature(K_DIGSIG_CERTIFICATE, K_DIGSIG_PRIVATE_KEY, K_DIGSIG_PASSWORD, K_DIGSIG_EXTRA_CERTS, K_DIGSIG_CERT_TYPE, array('Name'=>K_DIGSIG_NAME, 'Location'=>K_DIGSIG_LOCATION, 'Reason'=>K_DIGSIG_REASON, 'ContactInfo'=>K_DIGSIG_CONTACT));
}

// calculate some sizes
$cell_height_ratio = (K_CELL_HEIGHT_RATIO + 0.1);
$page_width = $pdf->getPageWidth() - PDF_MARGIN_LEFT - PDF_MARGIN_RIGHT;
$data_cell_height = round(($cell_height_ratio * PDF_FONT_SIZE_DATA) / $pdf->getScaleFactor(), 2);
$main_cell_height = round(($cell_height_ratio * PDF_FONT_SIZE_MAIN) / $pdf->getScaleFactor(), 2);
$data_cell_width = round($page_width / $page_elements, 2);
$data_cell_width_third = round($data_cell_width / 3, 2);
$data_cell_width_half = round($data_cell_width / 2, 2);

// set style for QR-Code (used for test data)
$qrstyle = array(
	'border' => false,
	'vpadding' => 0,
	'hpadding' => 0,
	'fgcolor' => array(0,0,0),
	'bgcolor' => false,
	'position' => 'C',
	'module_width' => 2,
	'module_height' => 2
);

// define some constants used to build the OMR grid
$grid_color = array(255, 0, 0);
$grid_bg_color = array(255,205,205);
$circle_bg_color = array(255,255,255);
$line_width = 0.177; // about half point
$circle_radius = ($line_width * 11);
$circle_width = (2 * $circle_radius) + $line_width;
$circle_shift = $circle_width + $line_width;
$circle_half_width = ($circle_width / 2);
$align_mark_color = array(0,0,0);
$align_mark_width = ($line_width * 7);
$align_mark_lenght = ($line_width * 22);
$align_mark_shift = ($line_width * 8);
$row_height = $circle_width + (8 * $line_width);
$line_style = array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color' => $grid_color);
// define barcode style
$bstyle = array(
	'position' => '',
	'align' => 'C',
	'stretch' => false,
	'fitwidth' => false,
	'cellfitalign' => '',
	'border' => false,
	'hpadding' => 0,
	'vpadding' => 0,
	'fgcolor' => array(0,0,0),
	'bgcolor' => false,
	'text' => false
);

// get test data
$testdata = F_getTestData($test_id);
$test_random_questions_select = F_getBoolean($testdata['test_random_questions_select']);
$test_random_questions_order = F_getBoolean($testdata['test_random_questions_order']);
$test_questions_order_mode = intval($testdata['test_questions_order_mode']);
$test_random_answers_select = F_getBoolean($testdata['test_random_answers_select']);
$test_random_answers_order = F_getBoolean($testdata['test_random_answers_order']);
$test_answers_order_mode = intval($testdata['test_answers_order_mode']);
$random_questions = ($test_random_questions_select OR $test_random_questions_order);
$sql_answer_position = '';
if (!$test_random_answers_order AND ($test_answers_order_mode == 0)) {
	$sql_answer_position = ' AND answer_position>0';
}
$sql_questions_order_by = '';
switch ($test_questions_order_mode) {
	case 0: { // position
		$sql_questions_order_by = ' AND question_position>0 ORDER BY question_position';
		break;
	}
	case 1: { // alphabetic
		$sql_questions_order_by = ' ORDER BY question_description';
		break;
	}
	case 2: { // ID
		$sql_questions_order_by = ' ORDER BY question_id';
		break;
	}
	case 3: { // type
		$sql_questions_order_by = ' ORDER BY question_type';
		break;
	}
	case 4: { // subject ID
		$sql_questions_order_by = ' ORDER BY question_subject_id';
		break;
	}
}

// NOTE: PDF tests are always random

for ($item = 1; $item <= $test_num; $item++) {
	// generate $test_num tests

	$pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
	
	// data to be printed as QR-Code to be later used as input from scanner/image
	$barcode_test_data = array();
	$barcode_test_data[0] = $test_id;

	// --- start page data ---
	$pdf->AddPage();

	$test_ref = $test_id.':'.$item.':'.date(K_TIMESTAMP_FORMAT);

	// set barcode
	$pdf->setBarcode($test_ref);

	$pdf->SetFillColor(204, 204, 204);
	$pdf->SetLineWidth(0.1);
	$pdf->SetDrawColor(0, 0, 0);
	$pdf->SetTextColor(0,0,0);

	// print document name (title)
	$pdf->SetFont(PDF_FONT_NAME_DATA, 'B', PDF_FONT_SIZE_DATA * K_TITLE_MAGNIFICATION);
	$pdf->Cell(0, $main_cell_height * K_TITLE_MAGNIFICATION, $doc_title, 1, 1, 'C', 1);

	$pdf->SetTextColor(255,0,0);
	$pdf->SetFont(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA);
	$pdf->Cell(0, 5, '['.$test_ref.']', 0, 1, 'C', false, '', 0, false, 'T', 'M');
	$pdf->SetTextColor(0,0,0);

	//$pdf->Ln(5);

	// display user info input boxes

	// calculate some sizes
	$user_elements = 4;
	$user_data_cell_width = round($page_width / $user_elements, 2);

	// print table headings
	$pdf->SetFont(PDF_FONT_NAME_DATA, 'B', PDF_FONT_SIZE_DATA);

	$pdf->Cell($user_data_cell_width, $data_cell_height, $l['w_lastname'], 1, 0, 'C', 1);
	$pdf->Cell($user_data_cell_width, $data_cell_height, $l['w_firstname'], 1, 0, 'C', 1);
	$pdf->Cell($user_data_cell_width, $data_cell_height, $l['w_code'], 1, 0, 'C', 1);
	$pdf->Cell($user_data_cell_width, $data_cell_height, $l['w_score'], 1, 1, 'C', 1);

	$pdf->SetFont(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA);

	$pdf->Cell($user_data_cell_width, (1.5 * $data_cell_height), '', 1, 0, 'C', 0);
	$pdf->Cell($user_data_cell_width, (1.5 * $data_cell_height), '', 1, 0, 'C', 0);
	$pdf->Cell($user_data_cell_width, (1.5 * $data_cell_height), '', 1, 0, 'C', 0);
	$pdf->Cell($user_data_cell_width, (1.5 * $data_cell_height), '', 1, 1, 'C', 0);

	$pdf->Ln(5);

	// --- display test info ---

	$info_cell_width = round($page_width / 4, 2);

	$boxStartY = $pdf->GetY(); // store current Y position

	// test name
	$pdf->SetFont(PDF_FONT_NAME_DATA, 'B', PDF_FONT_SIZE_DATA * HEAD_MAGNIFICATION);
	$pdf->Cell($page_width, $data_cell_height * HEAD_MAGNIFICATION, $l['w_test'].': '.$testdata['test_name'], 1, 1, '', 1);

	$pdf->SetFont(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA);

	$infoStartY = $pdf->GetY() + 2; // store current Y position
	$pdf->SetY($infoStartY);

	// test duration
	$pdf->Cell(1.5*$data_cell_width, $data_cell_height, $l['w_test_time'].' ['.$l['w_minutes'].']: ', 0, 0, $dirlabel, 0);
	$pdf->Cell($data_cell_width, $data_cell_height, $testdata['test_duration_time'], 0, 1, $dirvalue, 0);

	// test start time (to be compiled by the user)
	$pdf->Cell(1.5*$data_cell_width, $data_cell_height, $l['w_time_begin'].': ', 0, 0, $dirlabel, 0);
	$pdf->Cell($data_cell_width, $data_cell_height, '', 0, 1, $dirvalue, 0);

	// test end time (to be compiled by the user)
	$pdf->Cell(1.5*$data_cell_width, $data_cell_height, $l['w_time_end'].': ', 0, 0, $dirlabel, 0);
	$pdf->Cell($data_cell_width, $data_cell_height, '', 0, 1, $dirvalue, 0);

	// score for right answer
	$pdf->Cell(1.5*$data_cell_width, $data_cell_height, $l['w_score_right'].': ', 0, 0, $dirlabel, 0);
	$pdf->Cell($data_cell_width, $data_cell_height, $testdata['test_score_right'], 0, 1, $dirvalue, 0);

	// score for wrong answer
	$pdf->Cell(1.5*$data_cell_width, $data_cell_height, $l['w_score_wrong'].': ', 0, 0, $dirlabel, 0);
	$pdf->Cell($data_cell_width, $data_cell_height, $testdata['test_score_wrong'], 0, 1, $dirvalue, 0);

	// score for missing answer
	$pdf->Cell(1.5*$data_cell_width, $data_cell_height, $l['w_score_unanswered'].': ', 0, 0, $dirlabel, 0);
	$pdf->Cell($data_cell_width, $data_cell_height, $testdata['test_score_unanswered'], 0, 1, $dirvalue, 0);

	// max score
	$pdf->Cell(1.5*$data_cell_width, $data_cell_height, $l['w_max_score'].': ', 0, 0, $dirlabel, 0);
	$pdf->Cell($data_cell_width, $data_cell_height, $testdata['test_max_score'], 0, 1, $dirvalue, 0);

	// minimum required score to pass the exam
	if ($testdata['test_score_threshold'] > 0) {
		$pdf->Cell(1.5*$data_cell_width, $data_cell_height, $l['w_test_score_threshold'].': ', 0, 0, $dirlabel, 0);
		$pdf->Cell($data_cell_width, $data_cell_height, $testdata['test_score_threshold'], 0, 1, $dirvalue, 0);
	}

	$boxEndY = $pdf->GetY();

	$pdf->SetFont(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA);

	// test description
	$pdf->writeHTMLCell(0, ($boxEndY - $infoStartY + 4), (PDF_MARGIN_LEFT + ($info_cell_width * 2)), $infoStartY - 2, F_decode_tcecode($testdata['test_description']), 1, 1);

	// print box around test info
	$pdf->SetY($boxStartY);
	$pdf->Cell($page_width, ($boxEndY - $boxStartY + 2), '', 1, 1, 'C', 0);

	// --- end test info ---

	$pdf->Ln(5);

	/*
	$pdf->SetFont(PDF_FONT_NAME_DATA, 'B', PDF_FONT_SIZE_DATA);
	$pdf->Cell($data_cell_width_third, $data_cell_height, "#", 1, 0, 'C', 1);
	$pdf->Cell($data_cell_width_third, $data_cell_height, $l['w_score'], 1, 0, 'C', 1);
	$pdf->Cell(0, $data_cell_height, $l['w_question'], 1, 1, 'C', 1);
	$pdf->Ln($data_cell_height);
	$pdf->SetFont(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA);
	*/
	
	// IDs of MCSA questions with more than one correct answer
	$right_answers_mcsa_questions_ids = '';
	// IDs of MCSA questions with more than one wrong answer
	$wrong_answers_mcsa_questions_ids = array();
	// IDs of MCMA questions with more than one answer
	$answers_mcma_questions_ids = array();
	// IDs of ORDER questions with more than one ordering answer
	$answers_order_questions_ids = '';

	// count questions
	$itemcount = 1; 

	// *-*-*-*-*

	// selected questions IDs
	$selected_questions = '0';
	// 2. for each set of subjects
	// ------------------------------
	$sql = 'SELECT *
		FROM '.K_TABLE_TEST_SUBJSET.'
		WHERE tsubset_test_id='.$test_id.'
		ORDER BY tsubset_type, tsubset_difficulty, tsubset_answers DESC';
	if ($r = F_db_query($sql, $db)) {
		$questions_data = array();
		while ($m = F_db_fetch_array($r)) {
			// 3. select the subjects IDs
			$selected_subjects = '0';
			$sqlt = 'SELECT subjset_subject_id FROM '.K_TABLE_SUBJECT_SET.' WHERE subjset_tsubset_id='.$m['tsubset_id'];
			if ($rt = F_db_query($sqlt, $db)) {
				while ($mt = F_db_fetch_array($rt)) {
					$selected_subjects .= ','.$mt['subjset_subject_id'];
				}
			}
			// 4. select questions
			// ------------------------------
			$sqlq = 'SELECT question_id, question_type, question_difficulty, question_position, question_description
				FROM '.K_TABLE_QUESTIONS.'';
			$sqlq .= ' WHERE question_subject_id IN ('.$selected_subjects.')
				AND question_difficulty='.$m['tsubset_difficulty'].'
				AND question_enabled=\'1\'
				AND question_id NOT IN ('.$selected_questions.')';
			if ($m['tsubset_type'] > 0) {
				$sqlq .= ' AND question_type='.$m['tsubset_type'];
			}
			if ($m['tsubset_type'] == 1) {
				// (MCSA : Multiple Choice Single Answer) ----------
				// get questions with the right number of answers
				if (empty($right_answers_mcsa_questions_ids)) {
					$right_answers_mcsa_questions_ids = '0';
					$sqlt = 'SELECT DISTINCT answer_question_id FROM '.K_TABLE_ANSWERS.' WHERE answer_enabled=\'1\' AND answer_isright=\'1\''.$sql_answer_position.'';
					if ($rt = F_db_query($sqlt, $db)) {
						while ($mt = F_db_fetch_array($rt)) {
							$right_answers_mcsa_questions_ids .= ','.$mt['answer_question_id'];
						}
					}
				}
				$sqlq .= ' AND question_id IN ('.$right_answers_mcsa_questions_ids.')';
				if ($m['tsubset_answers'] > 0) {
					if (!isset($wrong_answers_mcsa_questions_ids['\''.$m['tsubset_answers'].'\''])) {
						$wrong_answers_mcsa_questions_ids['\''.$m['tsubset_answers'].'\''] = '0';
						$sqlt = 'SELECT answer_question_id FROM '.K_TABLE_ANSWERS.' WHERE answer_enabled=\'1\' AND answer_isright=\'0\''.$sql_answer_position.' GROUP BY answer_question_id HAVING (COUNT(answer_id)>='.($m['tsubset_answers']-1).')';
						if ($rt = F_db_query($sqlt, $db)) {
							while ($mt = F_db_fetch_array($rt)) {
								$wrong_answers_mcsa_questions_ids['\''.$m['tsubset_answers'].'\''] .= ','.$mt['answer_question_id'];
							}
						}
					}
					$sqlq .= ' AND question_id IN ('.$wrong_answers_mcsa_questions_ids['\''.$m['tsubset_answers'].'\''].')';
				}
			} elseif ($m['tsubset_type'] == 2) {
				// (MCMA : Multiple Choice Multiple Answers) -------
				// get questions with the right number of answers
				if ($m['tsubset_answers'] > 0) {
					if (!isset($answers_mcma_questions_ids['\''.$m['tsubset_answers'].'\''])) {
						$answers_mcma_questions_ids['\''.$m['tsubset_answers'].'\''] = '0';
						$sqlt = 'SELECT answer_question_id FROM '.K_TABLE_ANSWERS.' WHERE answer_enabled=\'1\''.$sql_answer_position.' GROUP BY answer_question_id HAVING (COUNT(answer_id)>='.$m['tsubset_answers'].')';
						if ($rt = F_db_query($sqlt, $db)) {
							while ($mt = F_db_fetch_array($rt)) {
								$answers_mcma_questions_ids['\''.$m['tsubset_answers'].'\''] .= ','.$mt['answer_question_id'];
							}
						}
					}
					$sqlq .= ' AND question_id IN ('.$answers_mcma_questions_ids['\''.$m['tsubset_answers'].'\''].')';
				}
			} elseif ($m['tsubset_type'] == 4) {
				// ORDERING ----------------------------------------
				if (empty($answers_order_questions_ids)) {
					$answers_order_questions_ids = '0';
					$sqlt = 'SELECT answer_question_id FROM '.K_TABLE_ANSWERS.' WHERE answer_enabled=\'1\' AND answer_position>0 GROUP BY answer_question_id HAVING (COUNT(answer_id)>1)';
					if ($rt = F_db_query($sqlt, $db)) {
						while ($mt = F_db_fetch_array($rt)) {
							$answers_order_questions_ids .= ','.$mt['answer_question_id'];
						}
					}
				}
				$sqlq .= ' AND question_id IN ('.$answers_order_questions_ids.')';
			}
			if ($random_questions) {
				$sqlq .= ' ORDER BY RAND()';
			} else {
				$sqlq .= $sql_questions_order_by;
			}
			if (K_DATABASE_TYPE == 'ORACLE') {
				$sqlq = 'SELECT * FROM ('.$sqlq.') WHERE rownum <= '.$m['tsubset_quantity'].'';
			} else {
				$sqlq .= ' LIMIT '.$m['tsubset_quantity'].'';
			}
			if ($rq = F_db_query($sqlq, $db)) {
				while ($mq = F_db_fetch_array($rq)) {
					// store questions data
					$tmp_data = array(
						'id' => $mq['question_id'],
						'type' => $mq['question_type'],
						'difficulty' => $mq['question_difficulty'],
						'description' => $mq['question_description'],
						'answers' => $m['tsubset_answers'],
						'score' => ($testdata['test_score_unanswered'] * $mq['question_difficulty'])
						);
					if ($random_questions OR ($test_questions_order_mode != 0)) {
						$questions_data[] = $tmp_data;
					} else {
						$questions_data[$mq['question_position']] = $tmp_data;
					}
					$selected_questions .= ','.$mq['question_id'].'';
				} // end while select questions
			} else {
				F_display_db_error(false);
				return false;
			} // --- end 3

		} // end while for each set of subjects
		// 5. STORE QUESTIONS AND ANSWERS
		// ------------------------------
		if ($random_questions) {
			shuffle($questions_data);
		} else {
			ksort($questions_data);
		}

		// *-*-*-*-*

		// 4. PRINT QUESTIONS
		// ------------------------------
		$question_order = 0;
		foreach ($questions_data as $key => $q) {
			++$question_order;

			// add question ID to QR-Code data
			$barcode_test_data[$question_order] = array(0 => $q['id'], 1 => array());

			// start transaction
			$pdf->startTransaction();
			$block_page = $pdf->getPage();
			$print_block = 2; // 2 tries max
			while ($print_block > 0) {

				// ------------------------------
				// add question number
				$pdf->Cell($data_cell_width_third, $data_cell_height, ''.$itemcount.' '.$qtype[($q['type']-1)].'', 1, 0, 'R', 0);
				// add max points
				$pdf->Cell($data_cell_width_third, $data_cell_height, ''.($q['difficulty'] * $testdata['test_score_right']).'', 1, 0, 'R', 0);
				// add question description
				$pdf->writeHTMLCell(0, $data_cell_height, (PDF_MARGIN_LEFT + (2 * $data_cell_width_third)), $pdf->GetY(), F_decode_tcecode($q['description']), 1, 1);
				// ------------------------------

				// do not split BLOCKS in multiple pages
				if ($pdf->getPage() == $block_page) {
					$print_block = 0;
				} else {
					// rolls back to the last (re)start
					$pdf = $pdf->rollbackTransaction();
					$pdf->AddPage();
					$block_page = $pdf->getPage();
					--$print_block;
				}
			} // end while print_block

			$itemcount++;

			if ($q['type'] == 3) {
				// print space for user text answer
				$restspace = $pdf->getPageHeight() - $pdf->GetY() - $pdf->getBreakMargin();
				$pdf->Cell(2*$data_cell_width_third, $data_cell_height, '', 0, 0, 'R', 0);

				// get the list of short answers
				$shortanswers = '';
				$sqlsa = 'SELECT answer_description
					FROM '.K_TABLE_ANSWERS.'
					WHERE answer_question_id='.$q['id'].'
						AND answer_enabled=\'1\'
						AND answer_isright=\'1\'';
				if($rsa = F_db_query($sqlsa, $db)) {
					while($msa = F_db_fetch_array($rsa)) {
						$shortanswers .= ''.$msa['answer_description'].' ; ';
					}
				} else {
					F_display_db_error();
				}

				// print correct answer in hidden white color
				/* to display the correct results, from PDF viewer, go to "Accessibility" ->
				   "Page Display preferences", check "Replace Document Colors",
				   uncheck "Only change the color of black text or line art" */
				$pdf->SetTextColor(255, 255, 255, false);
				if ($restspace > PDF_TEXTANSWER_HEIGHT) {
					$pdf->MultiCell(0, PDF_TEXTANSWER_HEIGHT, $shortanswers, 1, '', false, 1, '', '', true, 0, false, true, 0, 'T', false);
				} else {
					// split text area across two pages
					$pdf->Cell(0, $restspace, '', 'LTR', 1, 'C', 0);
					$pdf->Cell(2*$data_cell_width_third, $data_cell_height, '', 0, 0, 'R', 0);
					$pdf->MultiCell(0, (PDF_TEXTANSWER_HEIGHT - $restspace), $shortanswers, 'LRB', '', false, 1, '', '', true, 0, false, true, 0, 'T', false);
				}
				$pdf->SetTextColor(0, 0, 0, false);
				$pdf->Ln($data_cell_height);
			} else {
				// for each question
				$randorder = $test_random_answers_order;
				$answers_ids = array(); // array used to store answers IDs
				switch ($q['type']) {
					case 1: { // MCSA
						// select first right answer
						$answers_ids += F_selectAnswers($q['id'], 1, false, 1, 0, $randorder, $test_answers_order_mode);
						// select remaining answers
						$answers_ids += F_selectAnswers($q['id'], 0, false, ($q['answers'] - 1), 1, $randorder, $test_answers_order_mode);
						break;
					}
					case 2: { // MCMA
						// select answers
						$answers_ids += F_selectAnswers($q['id'], '', false, $q['answers'], 0, $randorder, $test_answers_order_mode);
						break;
					}
					case 4: { // ORDERING
						// select answers
						$randorder = true;
						$answers_ids += F_selectAnswers($q['id'], '', true, 0, 0, $randorder, $test_answers_order_mode);
						break;
					}
				}
				// randomizes the order of the answers
				if ($randorder) {
					shuffle($answers_ids);
				} else {
					ksort($answers_ids);
				}
				// print answers
				// add answers
				$answ_id = 0;
				// display multiple answers
				while (list($key, $answer_id) = each($answers_ids)) {
					++$answ_id;

					// add answer ID to QR-Code data
					$barcode_test_data[$question_order][1][$answ_id] = $answer_id;

					// display each answer option
					$sqla = 'SELECT *
						FROM '.K_TABLE_ANSWERS.'
						WHERE answer_id='.$answer_id.'
						LIMIT 1';
					if($ra = F_db_query($sqla, $db)) {
						if($ma = F_db_fetch_array($ra)) {
							$rightanswer = '';
							if ($mq['question_type'] == 4) {
								$rightanswer = $ma['answer_position'];
							} elseif (F_getBoolean($ma['answer_isright'])) {
								$rightanswer = 'X';
							}

							// start transaction
							$pdf->startTransaction();
							$block_page = $pdf->getPage();
							$print_block = 2; // 2 tries max
							while ($print_block > 0) {

								// ------------------------------
								$pdf->Cell(2*$data_cell_width_third, $data_cell_height, '', 0, 0, 'C', 0);
								// print correct answer in hidden white color
								/* to display the correct results, from PDF viewer, go to "Accessibility" ->
								   "Page Display preferences", check "Replace Document Colors",
								   uncheck "Only change the color of black text or line art" */
								$pdf->SetTextColor(255, 255, 255, false);
								$pdf->Cell($data_cell_width_third, $data_cell_height, $rightanswer, 1, 0, 'C', 0);
								$pdf->SetTextColor(0, 0, 0, false);
								$pdf->Cell($data_cell_width_third, $data_cell_height, $answ_id, 1, 0, 'R', 0);
								$pdf->writeHTMLCell(0, $data_cell_height, (PDF_MARGIN_LEFT + $data_cell_width + $data_cell_width_third), $pdf->GetY(), F_decode_tcecode($ma['answer_description']), 1, 1);
								// ------------------------------

								// do not split BLOCKS in multiple pages
								if ($pdf->getPage() == $block_page) {
									$print_block = 0;
								} else {
									// rolls back to the last (re)start
									$pdf = $pdf->rollbackTransaction();
									$pdf->AddPage();
									$block_page = $pdf->getPage();
									--$print_block;
								}
							} // end while print_block

						}
					} else {
						F_display_db_error();
					}
				}
				$pdf->Ln($data_cell_height);
			} // not-text question
		} // end while type of questions
	} else {
		F_display_db_error();
	}

	// *** OMR SECTION (Optical Mark Recognition) ******************************
	// Support up to 30 questions per sheet and up to 12 answers per questions.
	// Support only MCSA and MCMA questions.

	// remove default barcodes from header and footer to not interfere with data QR-code
	$pdf->resetHeaderTemplate();
	$pdf->setTCExamBackLink('');
	$pdf->AddPage('P'); // force portrait mode
	$pdf->setBarcode('');

	$pdf->SetTextColor(255,0,0);
	$pdf->SetFont(PDF_FONT_NAME_DATA, '', round(PDF_FONT_SIZE_DATA * 1.5));
	$pdf->Cell(0, 0, 'OMR DATA', 0, 1, 'C', false, '', 0, false, 'T', 'M');
	$pdf->SetFont(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA);
	$pdf->Cell(0, 0, '['.$test_ref.']', 0, 1, 'C', false, '', 0, false, 'T', 'M');
	$pdf->SetTextColor(0,0,0);

	// encode data to be printed on QR-Code
	$qr_test_data = F_encodeOMRTestData($barcode_test_data);

	$pagedim = $pdf->getPageDimensions();
	$qrw = $pagedim['wk'] - $pagedim['lm'] - $pagedim['rm']; // maximum width
	$qry = (($pagedim['hk'] - $qrw) / 2); // vertically centered on page

	// QR-CODE mode H (best error correction)
	// This will be used to create test logs
	$pdf->write2DBarcode($qr_test_data, 'QRCODE,L', '', $qry, '', '', $qrstyle, 'N');

	// --- OMR ANSWER SHEET ---------------------------------------------------

	// Instructions:
	// mark the correct/true answers
	// use blue/black marker
	// fill the box completely
	// make no stray marks

	// center the block on the page
	$start_x = (($pagedim['wk'] - 173.964) / 2);
	$start_y = (($pagedim['hk'] - 178.062) / 2);

	$pdf->SetLineStyle($line_style);
	$pdf->SetTextColorArray($grid_color);
	$pdf->SetCellPadding($line_width);

	// number of required OMR pages
	$num_questions = count($barcode_test_data) - 1;
	$num_omr_pages = ceil($num_questions / 30); // max 30 questions per page

	// remove barcode from footer
	$pdf->setBarcode('');

	// set style for barcodes containing first question number
	$bcstyle = array(
		'position' => 'C',
		'align' => 'C',
		'stretch' => false,
		'fitwidth' => false,
		'cellfitalign' => '',
		'border' => false,
		'padding' => 0,
		'fgcolor' => array(0,0,0),
		'bgcolor' => false,
		'text' => false
	);
	// barcode y position
	$bcy = $pagedim['hk'] - $pdf->getFooterMargin() - 12;

	// disable auto-page-break
	$pdf->SetAutoPageBreak(false, 0);

	for ($omrpage = 0; $omrpage < $num_omr_pages; ++$omrpage) {
		$pdf->AddPage('P');

		$pdf->SetTextColor(255,0,0);
		$pdf->SetFont(PDF_FONT_NAME_DATA, '', round(PDF_FONT_SIZE_DATA * 1.5));
		$pdf->Cell(0, 0, 'OMR ANSWER SHEET '.($omrpage + 1), 0, 1, 'C', false, '', 0, false, 'T', 'M');
		$pdf->SetFont(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA);
		$pdf->Cell(0, 0, '['.$test_ref.']', 0, 1, 'C', false, '', 0, false, 'T', 'M');
		$pdf->SetTextColor(0,0,0);

		// disable RTL mode
		$rtl = $pdf->getRTL();
		$pdf->setRTL(false);

		$x = $start_x;
		$y = $start_y;

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

		// set the starting row number (starting question number)
		$first_question = (1 + (30 * $omrpage));
		$qnum = sprintf('%04d', $first_question);

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

		// top alignment marks for columns
		$x = $start_x;
		$pdf->Rect($x, $y, $align_mark_lenght, $align_mark_lenght, 'F', array(), $align_mark_color);
		$x += $align_mark_lenght + 9;
		$pdf->SetFont('helvetica', '', 10);
		for ($i = 0; $i < 12; ++$i) {
			// vertical alignment mark
			$x += $circle_shift;
			$pdf->Rect($x + $align_mark_shift, $y, $align_mark_width, $align_mark_lenght, 'F', array(), $align_mark_color);
			$x += $circle_shift;
			$pdf->Rect($x + $align_mark_shift, $y, $align_mark_width, $align_mark_lenght, 'F', array(), $align_mark_color);
			$x += $circle_shift;
		}
		$y += ($row_height + $circle_half_width);

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

		for ($r=0; $r < 30; ++$r) {
			$current_question = ($first_question + $r);
			// start at left margin
			$x = $start_x;
			// center of circles
			$cy = $y + $circle_half_width;
			// left alignment mark for row
			$pdf->Rect($x, $y + $align_mark_shift, $align_mark_lenght, $align_mark_width, 'F', array(), $align_mark_color);
			$x += $align_mark_lenght;
			if ($current_question <= $num_questions) {
				if (($r % 2) != 0) {
					// row background
					$pdf->Rect($x, $y - (4 * $line_width), 166.176, $circle_width + (8 * $line_width), 'F', array(), $grid_bg_color);
				}
				// print question number
				$pdf->SetXY($x,$y);
				$pdf->SetFont('courier', 'B', 10);
				$pdf->SetTextColorArray($grid_color);
				$pdf->Cell(8, $circle_width, $current_question, 0, 0, 'R', false, '', 0, true, 'T', 'M');
				$x += 9;
				// question type
				$question_type = $questions_data[($current_question - 1)]['type'];
				if ($question_type < 3) { // MCSA or MCMA question
					// number of answers
					$num_answers = count($barcode_test_data[$current_question][1]);
					for ($i = 1; $i <= 12; ++$i) { // for each answer
						if ($i <= $num_answers) {
							// print answer number
							$pdf->SetXY($x,$y);
							$pdf->SetFont('helvetica', '', 8);
							$pdf->SetTextColorArray($grid_color);
							$pdf->Cell($circle_shift, $circle_width, $i, 0, 0, 'R', false, '', 0, true, 'T', 'M');
							$x += $circle_shift;
							// select circle
							$pdf->Circle($x + $circle_half_width, $cy, $circle_radius, 0, 360, 'DF', $line_style, $circle_bg_color, 2);
							$pdf->SetXY($x,$y);
							$pdf->SetFont(PDF_FONT_NAME_DATA, '', 6);
							$pdf->SetTextColorArray($grid_bg_color);
							$pdf->Cell($circle_width, $circle_width, $l['w_true_acronym'], 0, 0, 'C', false, '', 1, true, 'T', 'M');
							$x += $circle_shift;
							if ($question_type == 2) { // MCMA question
								$pdf->Circle($x + $circle_half_width, $cy, $circle_radius, 0, 360, 'DF', $line_style, $circle_bg_color, 2);
								$pdf->SetXY($x,$y);
								$pdf->Cell($circle_width, $circle_width, $l['w_false_acronym'], 0, 0, 'C', false, '', 1, true, 'T', 'M');

							}
						} else {
							$x += (2 * $circle_shift);
						}
						$x += $circle_shift;
					}
				} else {
					$x += (36 * $circle_shift);
				}
			} else {
				$x += 9 + (36 * $circle_shift);
			}
			$x += $circle_shift;
			// right alignment mark for row
			$pdf->Rect($x, $y + $align_mark_shift, $align_mark_lenght, $align_mark_width, 'F', array(), $align_mark_color);
			$y += $row_height;
		}

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

		// bottom alignment marks for columns
		$x = $start_x + $align_mark_lenght + 9;
		$y += $circle_half_width;
		$pdf->SetFont('helvetica', '', 10);
		for ($i = 0; $i < 12; ++$i) {
			// vertical alignment mark
			$x += $circle_shift;
			$pdf->Rect($x + $align_mark_shift, $y, $align_mark_width, $align_mark_lenght, 'F', array(), $align_mark_color);
			$x += $circle_shift;
			$pdf->Rect($x + $align_mark_shift, $y, $align_mark_width, $align_mark_lenght, 'F', array(), $align_mark_color);
			$x += $circle_shift;
		}

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

		// set barcode to identify starting question number
		$pdf->write1DBarcode($qnum, 'C128C', 0, $bcy, '', 10, 0.8, $bcstyle, '');

		// reset RTL mode
		$pdf->setRTL($rtl);
	} // end for each OMR page

} //end for test_num

$pdf->lastpage(true);
$pdf->SetAutoPageBreak(false);
$pdf->SetFont('helvetica', '', 5);
$pdf->SetTextColor(0,127,255);
$msg = "\x50\x6f\x77\x65\x72\x65\x64\x20\x62\x79\x20\x54\x43\x45\x78\x61\x6d\x20\x28\x77\x77\x77\x2e\x74\x63\x65\x78\x61\x6d\x2e\x6f\x72\x67\x29";
$lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x65\x78\x61\x6d\x2e\x6f\x72\x67";
$pdf->SetXY(15, $pdf->getPageHeight(), true);
$pdf->Cell(0, 0, $msg, 0, 0, 'R', 0, $lnk, 0, false, 'B', 'B');

// close and outputs PDF document
$pdf->Output('tcexam_test_'.$test_id.'_'.date('YmdHis').'.pdf', 'D');

//============================================================+
// END OF FILE
//============================================================+
