Commit fc0133eb by Pedro Bueno

Ajuste nos arquivos

parent 996791ac
from flask import Flask, render_template, request, send_file, redirect from flask import Flask, render_template, request, send_file
import fitz, os, zipfile, sqlite3 import io
import fitz # PyMuPDF
import os
import zipfile
from PIL import Image from PIL import Image
import sqlite3
app = Flask(__name__) app = Flask(__name__)
UPLOAD_FOLDER = 'uploads' UPLOAD_FOLDER = 'uploads'
os.makedirs(UPLOAD_FOLDER, exist_ok=True) os.makedirs(UPLOAD_FOLDER, exist_ok=True)
# Configuração do Banco de Dados (CRUD)
def init_db(): def init_db():
conn = sqlite3.connect('database.db') conn = sqlite3.connect('database.db')
cursor = conn.cursor() conn.execute('''CREATE TABLE IF NOT EXISTS configuracoes
cursor.execute('''CREATE TABLE IF NOT EXISTS configuracoes
(id INTEGER PRIMARY KEY AUTOINCREMENT, nome TEXT, coords TEXT)''') (id INTEGER PRIMARY KEY AUTOINCREMENT, nome TEXT, coords TEXT)''')
conn.commit()
conn.close() conn.close()
@app.route('/') @app.route('/')
...@@ -22,42 +23,59 @@ def index(): ...@@ -22,42 +23,59 @@ def index():
conn.close() conn.close()
return render_template('index.html', configs=configs) return render_template('index.html', configs=configs)
@app.route('/salvar', methods=['POST']) @app.route('/upload_preview', methods=['POST'])
def salvar_config(): def upload_preview():
nome = request.form['nome_config'] if 'pdf' not in request.files:
coords = request.form['coords'] return {"error": "Sem arquivo"}, 400
conn = sqlite3.connect('database.db') pdf = request.files['pdf']
conn.execute('INSERT INTO configuracoes (nome, coords) VALUES (?, ?)', (nome, coords)) pdf_path = os.path.join(UPLOAD_FOLDER, pdf.filename)
conn.commit() pdf.save(pdf_path)
conn.close() return {"filename": pdf.filename}
return redirect('/')
@app.route('/preview/<filename>/<int:pagnumber>')
def preview(filename, pagnumber):
pdf_path = os.path.join(UPLOAD_FOLDER, filename)
if not os.path.exists(pdf_path): return "Erro", 404
doc = fitz.open(pdf_path)
# Garante que a página solicitada existe no PDF
total = len(doc)
if pagnumber >= total: pagnumber = total - 1
if pagnumber < 0: pagnumber = 0
page = doc[pagnumber]
pix = page.get_pixmap(dpi=100)
img_data = pix.tobytes("png")
doc.close()
return send_file(io.BytesIO(img_data), mimetype='image/png')
@app.route('/processar', methods=['POST']) @app.route('/processar', methods=['POST'])
def processar(): def processar():
pdf = request.files['pdf'] filename = request.form['filename']
inicio = int(request.form['inicio']) inicio = int(request.form['inicio'])
fim = int(request.form['fim']) fim = int(request.form['fim'])
coords = list(map(float, request.form['coords'].split(','))) c = list(map(float, request.form['coords'].split(',')))
pdf_path = os.path.join(UPLOAD_FOLDER, pdf.filename)
pdf.save(pdf_path)
pdf_path = os.path.join(UPLOAD_FOLDER, filename)
doc = fitz.open(pdf_path) doc = fitz.open(pdf_path)
zip_path = os.path.join(UPLOAD_FOLDER, "resultado.zip") zip_path = os.path.join(UPLOAD_FOLDER, "recortes.zip")
with zipfile.ZipFile(zip_path, 'w') as zip_f: with zipfile.ZipFile(zip_path, 'w') as zip_f:
for p in range(inicio, fim + 1): for p in range(inicio, fim + 1):
if p >= len(doc): break if p >= len(doc): break
page = doc[p] # Lógica +1 integrada page = doc[p]
pix = page.get_pixmap(dpi=300) pix = page.get_pixmap(dpi=300)
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
fator = 300 / 72
area = (coords[0]*fator, coords[1]*fator, coords[2]*fator, coords[3]*fator) s = 300 / 100 # Escala Preview vs Corte Final
area = (c[0]*s, c[1]*s, (c[0]+c[2])*s, (c[1]+c[3])*s)
img_cortada = img.crop(area) img_cortada = img.crop(area)
img_tmp = f"pag_{p}.png" img_name = f"pagina_{p}.png"
img_cortada.save(img_tmp) img_path = os.path.join(UPLOAD_FOLDER, img_name)
zip_f.write(img_tmp) img_cortada.save(img_path)
os.remove(img_tmp) zip_f.write(img_path, img_name)
os.remove(img_path)
doc.close() doc.close()
return send_file(zip_path, as_attachment=True) return send_file(zip_path, as_attachment=True)
......
body { background: #121212; color: #eee; font-family: sans-serif; display: flex; justify-content: center; padding: 50px; } body { background: #121212; color: #eee; font-family: sans-serif; display: flex; justify-content: center; padding: 20px; }
.container { background: #1e1e1e; padding: 30px; border-radius: 12px; width: 450px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); } .container { background: #1e1e1e; padding: 30px; border-radius: 15px; width: 100%; max-width: 700px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); text-align: center; }
h1 { text-align: center; color: #4CAF50; } h1 { color: #4CAF50; }
input { width: 100%; padding: 10px; margin: 10px 0; background: #333; border: 1px solid #444; color: white; border-radius: 5px; box-sizing: border-box; } .custom-file-upload { border: 2px dashed #4CAF50; padding: 20px; cursor: pointer; display: block; border-radius: 10px; background: #252525; margin-bottom: 20px; }
.row { display: flex; gap: 10px; } .row { display: flex; gap: 15px; margin-bottom: 20px; }
button { width: 100%; padding: 12px; background: #4CAF50; border: none; color: white; font-weight: bold; border-radius: 5px; cursor: pointer; margin-top: 10px; } .input-group { flex: 1; text-align: left; }
button:hover { background: #45a049; } input[type="number"] { width: 100%; padding: 10px; background: #333; border: 1px solid #444; color: white; border-radius: 5px; }
hr { border: 0; border-top: 1px solid #333; margin: 20px 0; } .btn-primary { width: 100%; padding: 15px; background: #4CAF50; border: none; color: white; font-weight: bold; border-radius: 8px; cursor: pointer; }
.config-list { list-style: none; padding: 0; font-size: 14px; } .footer { margin-top: 20px; font-size: 11px; color: #666; }
.config-list li { background: #252525; padding: 10px; margin-bottom: 5px; border-radius: 4px; } \ No newline at end of file
.copyright { text-align: center; font-size: 10px; color: #666; margin-top: 20px; }
\ No newline at end of file
...@@ -2,40 +2,207 @@ ...@@ -2,40 +2,207 @@
<html lang="pt-br"> <html lang="pt-br">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>PDF Supremo CRUD</title> <title>PDF Visual - Pedro Bueno</title>
<link rel="stylesheet" href="/static/style.css"> <link rel="stylesheet" href="/static/style.css">
<style>
#preview-container {
display: none;
margin: 20px auto;
text-align: center;
}
#preview-wrapper {
position: relative;
display: inline-block;
border: 2px solid #555;
line-height: 0;
overflow: hidden; /* Garante que nada saia visualmente */
user-select: none;
}
#pdf-img {
max-width: 100%;
display: block;
}
#selector {
position: absolute;
border: 2px dashed #4CAF50;
background: rgba(76, 175, 80, 0.2);
cursor: move;
box-sizing: border-box;
width: 150px;
height: 150px;
top: 0;
left: 0;
}
#resizer {
position: absolute;
right: 0;
bottom: 0;
width: 15px;
height: 15px;
background: #4CAF50;
cursor: nwse-resize;
}
.nav-controls {
margin: 15px 0;
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
}
.nav-btn {
background: #444;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
}
.nav-btn:hover { background: #555; }
</style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1>Manipulador de PDF 🚀</h1> <h1>Recortador Visual 2.0 🚀</h1>
<label for="pdf-file" class="custom-file-upload">
📁 Selecionar PDF para Visualização
</label>
<input type="file" id="pdf-file" accept="application/pdf" onchange="uploadAndPreview()" style="display:none;">
<div id="loading" style="display:none; color: #4CAF50;">⏳ Carregando página...</div>
<div id="preview-container">
<div class="nav-controls">
<button type="button" class="nav-btn" onclick="changePage(-1)">⬅️ Anterior</button>
<span id="page-info">Página: 0</span>
<button type="button" class="nav-btn" onclick="changePage(1)">Próxima ➡️</button>
</div>
<div id="preview-wrapper">
<img id="pdf-img">
<div id="selector">
<div id="resizer"></div>
</div>
</div>
</div>
<form action="/processar" method="post" id="main-form">
<input type="hidden" name="filename" id="hidden-filename">
<input type="hidden" name="coords" id="coords-input">
<form action="/processar" method="post" enctype="multipart/form-data">
<input type="file" name="pdf" required>
<div class="row"> <div class="row">
<input type="number" name="inicio" placeholder="Início" value="10"> <div class="input-group">
<input type="number" name="fim" placeholder="Fim" value="11"> <label>Página Início:</label>
<input type="number" name="inicio" id="input-inicio" value="0">
</div>
<div class="input-group">
<label>Página Fim:</label>
<input type="number" name="fim" value="1">
</div> </div>
<input type="text" name="coords" id="coords_input" placeholder="x0, y0, x1, y1" value="0, 51, 700, 745"> </div>
<button type="submit" class="btn-primary">GERAR ZIP</button> <button type="submit" class="btn-primary">GERAR E BAIXAR ZIP</button>
</form> </form>
<p class="footer">© Pedro Bueno</p>
</div>
<hr> <script>
const selector = document.getElementById('selector');
const resizer = document.getElementById('resizer');
const img = document.getElementById('pdf-img');
const coordsInput = document.getElementById('coords-input');
const inputInicio = document.getElementById('input-inicio');
<h3>Salvar Configuração de Corte</h3> let currentPage = 0;
<form action="/salvar" method="post">
<input type="text" name="nome_config" placeholder="Nome (Ex: Livro de História)">
<input type="text" name="coords" placeholder="Coordenadas">
<button type="submit">Salvar no Banco</button>
</form>
<ul class="config-list"> async function uploadAndPreview() {
{% for config in configs %} const file = document.getElementById('pdf-file').files[0];
<li> if(!file) return;
<strong>{{ config[1] }}</strong>: {{ config[2] }}
</li>
{% endfor %}
</ul>
</div> document.getElementById('loading').style.display = 'block';
const formData = new FormData();
formData.append('pdf', file);
const res = await fetch('/upload_preview', { method: 'POST', body: formData });
const data = await res.json();
document.getElementById('hidden-filename').value = data.filename;
currentPage = parseInt(inputInicio.value) || 0;
updatePreview();
}
function updatePreview() {
const filename = document.getElementById('hidden-filename').value;
img.src = `/preview/${filename}/${currentPage}?t=${new Date().getTime()}`;
document.getElementById('page-info').innerText = `Visualizando Página: ${currentPage}`;
inputInicio.value = currentPage; // Sincroniza o form com a visão
img.onload = () => {
document.getElementById('loading').style.display = 'none';
document.getElementById('preview-container').style.display = 'block';
updateCoords();
};
}
function changePage(delta) {
currentPage += delta;
if (currentPage < 0) currentPage = 0;
updatePreview();
}
// Lógica de Arrastar e Redimensionar com Contenção
let isDragging = false, isResizing = false;
let startX, startY, startW, startH;
selector.onmousedown = (e) => {
if(e.target === resizer) return;
isDragging = true;
startX = e.clientX - selector.offsetLeft;
startY = e.clientY - selector.offsetTop;
};
resizer.onmousedown = (e) => {
isResizing = true;
startX = e.clientX;
startY = e.clientY;
startW = selector.offsetWidth;
startH = selector.offsetHeight;
e.stopPropagation();
e.preventDefault();
};
window.onmousemove = (e) => {
if (isDragging) {
let x = e.clientX - startX;
let y = e.clientY - startY;
// Bloqueia saída das bordas
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x + selector.offsetWidth > img.offsetWidth) x = img.offsetWidth - selector.offsetWidth;
if (y + selector.offsetHeight > img.offsetHeight) y = img.offsetHeight - selector.offsetHeight;
selector.style.left = x + 'px';
selector.style.top = y + 'px';
}
if (isResizing) {
let newWidth = startW + (e.clientX - startX);
let newHeight = startH + (e.clientY - startY);
// Bloqueia redimensionamento para fora da imagem
if (selector.offsetLeft + newWidth > img.offsetWidth) newWidth = img.offsetWidth - selector.offsetLeft;
if (selector.offsetTop + newHeight > img.offsetHeight) newHeight = img.offsetHeight - selector.offsetTop;
selector.style.width = Math.max(20, newWidth) + 'px';
selector.style.height = Math.max(20, newHeight) + 'px';
}
updateCoords();
};
window.onmouseup = () => { isDragging = false; isResizing = false; };
function updateCoords() {
coordsInput.value = `${selector.offsetLeft},${selector.offsetTop},${selector.offsetWidth},${selector.offsetHeight}`;
}
</script>
</body> </body>
</html> </html>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment