diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..8966324 --- /dev/null +++ b/.htaccess @@ -0,0 +1,9 @@ + + Header set Access-Control-Allow-Origin "*" + + + + Options Indexes MultiViews + AllowOverride All + Require all granted + diff --git a/config.php b/config.php new file mode 100644 index 0000000..70fdeab --- /dev/null +++ b/config.php @@ -0,0 +1,36 @@ +connect_error) { + die("Falha na conexão: " . $conn->connect_error); +} + +// Configurar charset +$conn->set_charset("utf8mb4"); + +// Configurar timezone +date_default_timezone_set('America/Sao_Paulo'); + +// Função para limpar input +function clean_input($data) { + global $conn; + $data = trim($data); + $data = stripslashes($data); + $data = htmlspecialchars($data); + return $conn->real_escape_string($data); +} + +// Função para verificar se é uma requisição AJAX +function is_ajax_request() { + return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && + strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; +} +?> diff --git a/delete_document.php b/delete_document.php new file mode 100644 index 0000000..9a36ac1 --- /dev/null +++ b/delete_document.php @@ -0,0 +1,36 @@ +prepare($sql); + $stmt->bind_param("i", $doc_id); + $stmt->execute(); + $result = $stmt->get_result(); + + if ($doc = $result->fetch_assoc()) { + // Deletar arquivo físico + if (file_exists($doc['arquivo'])) { + unlink($doc['arquivo']); + } + + // Deletar registro do banco + $sql = "DELETE FROM documentos WHERE id = ?"; + $stmt = $conn->prepare($sql); + $stmt->bind_param("i", $doc_id); + + if ($stmt->execute()) { + header("Location: index.php?page=doc_list&client_id=" . $doc['client_id'] . "&status=deleted"); + } else { + die("Erro ao excluir documento: " . $stmt->error); + } + } else { + die("Documento não encontrado"); + } +} else { + die("ID do documento não fornecido"); +} +?> diff --git a/docs.sql b/docs.sql new file mode 100644 index 0000000..0c12e3a --- /dev/null +++ b/docs.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS clientes ( + id INT AUTO_INCREMENT PRIMARY KEY, + nome VARCHAR(100) NOT NULL, + cpf VARCHAR(14) UNIQUE, + email VARCHAR(100), + telefone VARCHAR(20), + data_cadastro DATETIME DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS documentos ( + id INT AUTO_INCREMENT PRIMARY KEY, + client_id INT NOT NULL, + tipo VARCHAR(50) NOT NULL, + arquivo VARCHAR(255) NOT NULL, + data_upload DATETIME NOT NULL, + FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE, + INDEX idx_client_id (client_id) +) ENGINE=InnoDB; \ No newline at end of file diff --git a/document_detector.py b/document_detector.py new file mode 100644 index 0000000..a2135f4 --- /dev/null +++ b/document_detector.py @@ -0,0 +1,100 @@ +import cv2 +import numpy as np +import sys +import json +from pathlib import Path +import os +from PIL import Image + +def order_points(pts): + rect = np.zeros((4, 2), dtype="float32") + s = pts.sum(axis=1) + rect[0] = pts[np.argmin(s)] + rect[2] = pts[np.argmax(s)] + diff = np.diff(pts, axis=1) + rect[1] = pts[np.argmin(diff)] + rect[3] = pts[np.argmax(diff)] + return rect + +def process_document(image_path, doc_type, output_dir): + try: + # Carrega e verifica a imagem + image = cv2.imread(str(image_path)) + if image is None: + raise Exception("Não foi possível carregar a imagem") + + original = image.copy() + + # Pré-processamento + gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + blurred = cv2.GaussianBlur(gray, (5, 5), 0) + edges = cv2.Canny(blurred, 50, 150) + + # Encontra contornos + contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + if not contours: + raise Exception("Nenhum contorno encontrado") + + # Ordena contornos por área + contours = sorted(contours, key=cv2.contourArea, reverse=True) + doc_corners = None + + # Procura o documento + for contour in contours: + peri = cv2.arcLength(contour, True) + approx = cv2.approxPolyDP(contour, 0.02 * peri, True) + if len(approx) == 4: + doc_corners = approx + break + + if doc_corners is None: + raise Exception("Documento não encontrado") + + # Processa cantos e aplica transformação + doc_corners = doc_corners.reshape(4, 2) + ordered_corners = order_points(doc_corners) + + (tl, tr, br, bl) = ordered_corners + widthA = np.linalg.norm(br - bl) + widthB = np.linalg.norm(tr - tl) + maxWidth = max(int(widthA), int(widthB)) + + heightA = np.linalg.norm(tr - br) + heightB = np.linalg.norm(tl - bl) + maxHeight = max(int(heightA), int(heightB)) + + dst = np.array([ + [0, 0], + [maxWidth - 1, 0], + [maxWidth - 1, maxHeight - 1], + [0, maxHeight - 1] + ], dtype="float32") + + # Aplica transformação perspectiva + matrix = cv2.getPerspectiveTransform(ordered_corners, dst) + warped = cv2.warpPerspective(original, matrix, (maxWidth, maxHeight)) + + # Salva resultado + output_path = os.path.join(output_dir, f"{doc_type}_{Path(image_path).name}") + cv2.imwrite(str(output_path), warped) + + return { + "success": True, + "message": "Processamento concluído com sucesso", + "width": maxWidth, + "height": maxHeight + } + + except Exception as e: + return { + "success": False, + "message": str(e) + } + +if __name__ == "__main__": + if len(sys.argv) != 4: + print(json.dumps({"success": False, "message": "Uso: python document_detector.py "})) + sys.exit(1) + + result = process_document(Path(sys.argv[1]), sys.argv[2], Path(sys.argv[3])) + print(json.dumps(result)) diff --git a/document_history.php b/document_history.php new file mode 100644 index 0000000..5681ca4 --- /dev/null +++ b/document_history.php @@ -0,0 +1,85 @@ +connect_error) { + die("Connection failed: " . $mysqli->connect_error); +} + +// Buscar histórico de documentos processados +$query = "SELECT pd.*, u.username FROM processed_documents pd + LEFT JOIN users u ON pd.user_id = u.id + ORDER BY pd.created_at DESC"; +$result = $mysqli->query($query); +?> + +
+

Histórico de Documentos Processados

+ + + + + + + + + + + + + fetch_assoc()): ?> + + + + + + + + + +
Data/HoraUsuárioDocumento OriginalDocumento ProcessadoAções
+ + Ver Original + + + + Ver Processado + + + +
+
+ + diff --git a/editor.php b/editor.php new file mode 100644 index 0000000..9d8799a --- /dev/null +++ b/editor.php @@ -0,0 +1,694 @@ + + + + Editor de Documentos + + + + + + +
+
+ +
+ +
+
+ + +
+
+ +
+
+
+ + + 0 +
+
+ + + 0 +
+
+ + + 0 +
+
+ + + 0 +
+
+
+ +
+ + + + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/index.php b/index.php index 442e759..bea9a32 100644 --- a/index.php +++ b/index.php @@ -2,9 +2,10 @@ ob_start(); session_start(); +//require_once 'config.php'; // Verificar se o usuário está logado -if (!isset($_SESSION['user_id'])) { +if (!isset($_SESSION['user_id']) && !in_array($_GET['page'], ['login', 'logout'])) { header("Location: login.php"); exit(); } @@ -32,28 +33,84 @@ include 'top_menu.php'; Bem-vindo ao Sistema de Ferramentas"; + switch($page) { + case 'upload': + include 'prova/upload_prova.php'; + break; + case 'status': + include 'prova/status.php'; + break; + case 'list_files': + include 'prova/list_files.php'; + break; + case 'register_client': + include 'clientes/register_client.php'; + break; + case 'list_clients': + include 'clientes/list_clients.php'; + break; + case 'client_details': + if (isset($_GET['id'])) { + include 'clientes/client_details.php'; + } + break; + case 'edit_client': + if (isset($_GET['id'])) { + include 'clientes/edit_client.php'; + } + break; + case 'delete_client': + if (isset($_GET['id'])) { + include 'clientes/delete_client.php'; + } + break; + case 'edit_note': + if (isset($_POST['id'])) { + include 'clientes/edit_note.php'; + } + break; + case 'delete_note': + if (isset($_POST['id'])) { + include 'clientes/delete_note.php'; + } + break; + case 'doc_upload': + include 'upload.php'; + break; + case 'doc_list': + include 'list.php'; + break; + case 'delete_document': + if (isset($_GET['id'])) { + include 'delete_document.php'; + } + break; + case 'temp_upload': + header('Content-Type: application/json'); + require_once 'temp_upload.php'; + exit(); + break; + + case 'save_edited': + // Garante que nenhuma saída HTML será enviada + ob_clean(); + header('Content-Type: application/json'); + require_once 'save_edited.php'; + exit(); + break; + + case 'editor': + require_once 'editor.php'; + break; + + case 'auto_adjust': + header('Content-Type: application/json'); + require_once 'auto_adjust.php'; + exit(); + break; + default: + echo "

Bem-vindo ao Sistema de Ferramentas

"; + break; } } else { echo "

Bem-vindo ao Sistema de Ferramentas

"; diff --git a/list.php b/list.php new file mode 100644 index 0000000..5034573 --- /dev/null +++ b/list.php @@ -0,0 +1,133 @@ + + + + Documentos Processados + + + + +
+ \n"; + + if ($conn->connect_error) { + die("
Erro de conexão: " . $conn->connect_error . "
"); + } + + if (!isset($_GET['client_id'])) { + echo "

Selecione um cliente para ver seus documentos:

"; + $sql = "SELECT id, name FROM clients"; + echo "\n"; + + $result = $conn->query($sql); + + if ($result === false) { + echo "
Erro na query: " . $conn->error . "
"; + echo "\n"; + } else { + echo "\n"; + while ($row = $result->fetch_assoc()) { + echo "
"; + echo "
"; + echo "" . $row['name'] . ""; + echo "
"; + echo "
"; + echo "Ver documentos"; + echo "
"; + echo "
"; + } + } + } else { + $client_id = intval($_GET['client_id']); + echo "\n"; + + // Buscar documentos do cliente + $sql = "SELECT d.*, c.name as client_name + FROM documentos d + JOIN clients c ON d.client_id = c.id + WHERE d.client_id = ? + ORDER BY d.data_upload DESC"; + + echo "\n"; + + $stmt = $conn->prepare($sql); + if ($stmt === false) { + echo "
Erro ao preparar query: " . $conn->error . "
"; + } else { + $stmt->bind_param("i", $client_id); + $stmt->execute(); + $result = $stmt->get_result(); + + echo "\n"; + + if ($result->num_rows > 0) { + while ($doc = $result->fetch_assoc()) { + echo "
"; + echo "
"; + echo "Tipo: " . ucfirst($doc['tipo']) . "
"; + echo "Data: " . date("d/m/Y H:i", strtotime($doc['data_upload'])); + echo "
"; + echo "
"; + echo "Visualizar | "; + echo "Excluir"; + echo "
"; + echo "
"; + } + } else { + echo "

Nenhum documento encontrado para este cliente.

"; + } + } + } + ?> + + + +
+ +
+ +
+ + + + diff --git a/menu.php b/menu.php index afd199e..ecaf664 100644 --- a/menu.php +++ b/menu.php @@ -12,7 +12,7 @@ if (!isset($_SESSION['user_id'])) {
  • Autenticador de Provas Digitais @@ -24,6 +24,13 @@ if (!isset($_SESSION['user_id'])) {
  • Listagem de Clientes
  • +
  • + Processador de Documentos + +
  • diff --git a/process.php b/process.php new file mode 100644 index 0000000..bd67439 --- /dev/null +++ b/process.php @@ -0,0 +1,91 @@ +&1', + $python_script, + $original_path, + $doc_type, + $client_dir + ); + + // Log do comando + file_put_contents('python_debug.log', date('Y-m-d H:i:s') . "\nComando: " . $command . "\n", FILE_APPEND); + + // Executar comando + $output = []; + $return_var = 0; + exec($command, $output, $return_var); + + // Log da saída + file_put_contents('python_debug.log', "Saída: " . implode("\n", $output) . "\nCódigo: $return_var\n\n", FILE_APPEND); + + // Verificar resultado + $last_output = end($output); + $result = json_decode($last_output, true); + + if ($result && $result['success']) { + // Nome do arquivo processado + $processed_name = $doc_type . "_" . $original_name; + $processed_file = $client_dir . "/" . $processed_name; + + // Registrar no banco + $sql = "INSERT INTO documentos (client_id, tipo, arquivo, data_upload) VALUES (?, ?, ?, NOW())"; + $stmt = $conn->prepare($sql); + $stmt->bind_param("iss", $client_id, $doc_type, $processed_file); + + if (!$stmt->execute()) { + throw new Exception("Erro ao registrar documento: " . $stmt->error); + } + + header("Location: index.php?page=doc_list&client_id=" . $client_id . "&status=success"); + exit(); + } else { + throw new Exception("Erro no processamento: " . ($result['message'] ?? 'Erro desconhecido')); + } + + } catch (Exception $e) { + file_put_contents('error.log', date('Y-m-d H:i:s') . " - " . $e->getMessage() . "\n", FILE_APPEND); + die("Erro: " . $e->getMessage()); + } +} else { + die("Erro no upload: " . $_FILES["document"]["error"]); +} +?> diff --git a/save_edited.php b/save_edited.php new file mode 100644 index 0000000..9dba1ca --- /dev/null +++ b/save_edited.php @@ -0,0 +1,116 @@ +next_result()) { + if ($res = $conn->store_result()) { + $res->free(); + } + } + + // Verificar dados do POST e FILES + if (!isset($_POST['client_id']) || !isset($_FILES['image'])) { + throw new Exception("Dados incompletos"); + } + + // Debug + error_log("POST data: " . print_r($_POST, true)); + error_log("FILES data: " . print_r($_FILES, true)); + + $client_id = intval($_POST['client_id']); + $doc_type = $_POST['doc_type']; + + // Verificar cliente com nova conexão + $check_client = $conn->prepare("SELECT id FROM clients WHERE id = ? LIMIT 1"); + if (!$check_client) { + throw new Exception("Erro ao preparar consulta: " . $conn->error); + } + + $check_client->bind_param("i", $client_id); + $check_client->execute(); + $result = $check_client->get_result(); + + if ($result->num_rows === 0) { + throw new Exception("Cliente não encontrado"); + } + $check_client->close(); + + // Criar diretório do cliente + $client_dir = "processed/{$client_id}/"; + if (!file_exists($client_dir)) { + if (!mkdir($client_dir, 0777, true)) { + throw new Exception("Erro ao criar diretório"); + } + } + + // Verificar tipo do arquivo + $mime_type = $_FILES['image']['type']; + if ($mime_type !== 'application/pdf' && !strpos($mime_type, 'image/') === 0) { + throw new Exception("Tipo de arquivo não permitido"); + } + + // Gerar nome do arquivo sempre como PDF + $timestamp = time(); + $filename = sprintf( + '%s_%s_%s.pdf', + $doc_type, + $timestamp, + uniqid() + ); + $filepath = $client_dir . $filename; + + // Se for imagem, converter para PDF + if (strpos($mime_type, 'image/') === 0) { + require_once 'vendor/autoload.php'; // Requer tecnickcom/tcpdf + $pdf = new TCPDF(); + $pdf->AddPage(); + $pdf->Image($_FILES['image']['tmp_name'], 0, 0, $pdf->getPageWidth(), $pdf->getPageHeight()); + $pdf->Output($filepath, 'F'); + } else { + // Se já for PDF, apenas mover + move_uploaded_file($_FILES['image']['tmp_name'], $filepath); + } + + // Registrar no banco com nova preparação + $insert = $conn->prepare("INSERT INTO documentos (client_id, tipo, arquivo, data_upload) VALUES (?, ?, ?, NOW())"); + if (!$insert) { + throw new Exception("Erro ao preparar inserção: " . $conn->error); + } + + $insert->bind_param("iss", $client_id, $doc_type, $filepath); + + if (!$insert->execute()) { + unlink($filepath); + throw new Exception("Erro ao registrar documento: " . $insert->error); + } + + $doc_id = $insert->insert_id; + $insert->close(); + + // Retornar sucesso + echo json_encode([ + 'success' => true, + 'message' => 'Documento salvo com sucesso', + 'file' => $filepath, + 'id' => $doc_id + ]); + exit(); + +} catch (Exception $e) { + http_response_code(400); + echo json_encode([ + 'success' => false, + 'message' => $e->getMessage() + ]); + exit(); +} +?> diff --git a/style.css b/style.css index 4eb5acf..2723ee2 100644 --- a/style.css +++ b/style.css @@ -209,3 +209,50 @@ textarea { #search-button:hover { background-color: #0056b3; } + +/* Estilos para processamento de documentos */ +.upload-form { + max-width: 600px; + margin: 20px auto; + padding: 20px; + background: #f5f5f5; + border-radius: 5px; +} + +.result-preview { + margin-top: 30px; +} + +.image-comparison { + display: flex; + justify-content: space-around; + margin-top: 20px; + flex-wrap: wrap; +} + +.image-comparison img { + max-width: 45%; + height: auto; + margin: 10px; + border: 1px solid #ddd; + box-shadow: 0 0 5px rgba(0,0,0,0.2); +} + +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} + +.alert-success { + color: #155724; + background-color: #d4edda; + border-color: #c3e6cb; +} + +.alert-error { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; +} diff --git a/temp_upload.php b/temp_upload.php new file mode 100644 index 0000000..f91fcce --- /dev/null +++ b/temp_upload.php @@ -0,0 +1,120 @@ + $success, + 'message' => $message + ], $data)); + exit; +} + +try { + // Configurar diretório temporário com caminho absoluto + $temp_dir = __DIR__ . "/temp/"; + $web_temp_dir = "temp/"; // Para URLs + + // Criar diretório se não existir + if (!file_exists($temp_dir)) { + if (!mkdir($temp_dir, 0777, true)) { + throw new Exception("Erro ao criar diretório temporário"); + } + chmod($temp_dir, 0777); // Garantir permissões + } + + // Debug + error_log("POST: " . print_r($_POST, true)); + error_log("FILES: " . print_r($_FILES, true)); + + // Verificar se recebeu arquivos + if (empty($_FILES['document'])) { + throw new Exception("Nenhum arquivo enviado"); + } + + $files = []; + $uploaded_files = []; + + // Reorganizar array de arquivos + foreach ($_FILES['document']['name'] as $key => $name) { + $files[] = [ + 'name' => $_FILES['document']['name'][$key], + 'type' => $_FILES['document']['type'][$key], + 'tmp_name' => $_FILES['document']['tmp_name'][$key], + 'error' => $_FILES['document']['error'][$key], + 'size' => $_FILES['document']['size'][$key] + ]; + } + + // Processar cada arquivo + foreach ($files as $index => $file) { + if ($file['error'] !== UPLOAD_ERR_OK) { + continue; + } + + // Validar tipo + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mime_type = finfo_file($finfo, $file['tmp_name']); + finfo_close($finfo); + + if (!in_array($mime_type, ['image/jpeg', 'image/png', 'image/jpg'])) { + continue; + } + + // Gerar nome único para cada arquivo + $ext = pathinfo($file['name'], PATHINFO_EXTENSION); + $basename = uniqid('doc_') . '_' . time(); + $filename = sprintf('%s_%d.%s', $basename, $index, $ext); + + $temp_path = $temp_dir . $filename; + $web_path = $web_temp_dir . $filename; + + // Mover arquivo + if (move_uploaded_file($file['tmp_name'], $temp_path)) { + // Ajustar permissões do arquivo + chmod($temp_path, 0644); + + $uploaded_files[] = [ + 'url' => $web_path, + 'path' => $temp_path, + 'index' => $index + ]; + } + } + + if (empty($uploaded_files)) { + throw new Exception("Nenhum arquivo válido foi enviado"); + } + + // Debug + error_log("Arquivos processados: " . print_r($uploaded_files, true)); + + // Retornar sucesso com primeiro arquivo como principal + echo json_encode([ + 'success' => true, + 'message' => count($uploaded_files) . ' arquivo(s) processado(s)', + 'imageUrl' => $uploaded_files[0]['url'], + 'allImages' => $uploaded_files, + 'fileCount' => count($uploaded_files), + 'baseUrl' => $web_temp_dir, + 'basename' => $basename + ]); + +} catch (Exception $e) { + error_log("Erro no upload: " . $e->getMessage()); + echo json_encode([ + 'success' => false, + 'message' => $e->getMessage(), + 'debug' => [ + 'POST' => $_POST, + 'FILES' => isset($_FILES['document']) ? $_FILES['document'] : 'No files' + ] + ]); +} +?> diff --git a/upload.php b/upload.php new file mode 100644 index 0000000..2ba00ff --- /dev/null +++ b/upload.php @@ -0,0 +1,245 @@ + + + + + Upload de Documentos + + + + + + + + + + + +
    +

    Upload de Documentos

    + \n"; + require_once 'config.php'; + + if ($conn->connect_error) { + echo "
    Erro de conexão: " . $conn->connect_error . "
    "; + } else { + echo "\n"; + } + ?> +
    +

    +
    + +

    +

    +
    + + Você pode selecionar várias imagens para unir em um único documento +

    +

    +
    + +

    + +
    + +
    + + +
    + error; + } + ?> +
    +

    Ver documentos processados

    +
    + +
    +

    Resultado do Processamento:

    +
    +

    Documento Original:

    + Original +
    +
    +

    Documento Processado:

    + Processado +
    +
    + +
    + + + +