WallaceBrasil commited on
Commit
2991cae
·
verified ·
1 Parent(s): 2ab114b

Update processador.py

Browse files
Files changed (1) hide show
  1. processador.py +101 -195
processador.py CHANGED
@@ -1,195 +1,101 @@
1
- # processador.py
2
- from pdf2image import convert_from_bytes
3
- from typing import List, Tuple, Optional
4
- from pathlib import Path
5
- import io
6
- import shutil
7
- import tempfile
8
- import platform
9
- import zipfile
10
- import time
11
-
12
- # --- Poppler ---
13
- # Windows costuma precisar do caminho do Poppler; no Hugging Face (Linux) já está no PATH.
14
- if platform.system() == "Windows":
15
- POPPLER_PATH = r"C:\poppler-25.07.0\Library\bin" # ajuste se necessário no seu PC
16
- else:
17
- POPPLER_PATH = None # no HF não precisa
18
-
19
- def _kwargs_poppler():
20
- """Monta kwargs para o pdf2image dependendo da plataforma."""
21
- return {"poppler_path": POPPLER_PATH} if POPPLER_PATH else {}
22
-
23
- # ------------------------------------------------------------
24
- # Helpers de conversão por PDF (mantidos para reuso)
25
- # ------------------------------------------------------------
26
- def extrair_todas_as_paginas(
27
- pdf_bytes: bytes,
28
- nome_pdf: str,
29
- formato: str = "jpeg",
30
- dpi: int = 300
31
- ) -> Tuple[List[Tuple[int, io.BytesIO]], str]:
32
- """
33
- Converte TODAS as páginas do PDF para o formato escolhido.
34
- Retorna lista [(numero_pagina, buffer), ...] e caminho de um ZIP contendo SÓ este PDF.
35
- (No fluxo 'misto', o ZIP final será refeito juntando PDFs.)
36
- """
37
- pil_pages = convert_from_bytes(pdf_bytes, dpi=dpi, fmt="ppm", **_kwargs_poppler())
38
-
39
- resultado: List[Tuple[int, io.BytesIO]] = []
40
- for i, pil_img in enumerate(pil_pages, start=1):
41
- buf = io.BytesIO()
42
- img_rgb = pil_img.convert("RGB")
43
- if formato.lower() == "ico":
44
- img_rgb.save(buf, format="ICO")
45
- else:
46
- img_rgb.save(buf, format=formato.upper())
47
- buf.seek(0)
48
- resultado.append((i, buf))
49
-
50
- # ZIP individual (não usado no 'misto', mas mantemos por compatibilidade)
51
- zip_path = _salvar_e_zipar(resultado, nome_pdf, formato)
52
- return resultado, zip_path
53
-
54
- def extrair_paginas_especificas(
55
- pdf_bytes: bytes,
56
- nome_pdf: str,
57
- paginas: List[int],
58
- formato: str = "jpeg",
59
- dpi: int = 300
60
- ) -> Tuple[List[Tuple[int, io.BytesIO]], str]:
61
- """
62
- Converte apenas páginas específicas (ex.: [3, 5, 10]).
63
- Retorna lista [(numero_pagina, buffer), ...] e caminho de um ZIP só deste PDF.
64
- """
65
- paginas_ord = sorted(set(paginas))
66
- first, last = min(paginas_ord), max(paginas_ord)
67
-
68
- pil_interval = convert_from_bytes(
69
- pdf_bytes, dpi=dpi, first_page=first, last_page=last, fmt="ppm", **_kwargs_poppler()
70
- )
71
-
72
- resultado: List[Tuple[int, io.BytesIO]] = []
73
- for pagina in paginas_ord:
74
- idx = pagina - first # índice relativo dentro do intervalo
75
- buf = io.BytesIO()
76
- img_rgb = pil_interval[idx].convert("RGB")
77
- if formato.lower() == "ico":
78
- img_rgb.save(buf, format="ICO")
79
- else:
80
- img_rgb.save(buf, format=formato.upper())
81
- buf.seek(0)
82
- resultado.append((pagina, buf))
83
-
84
- zip_path = _salvar_e_zipar(resultado, nome_pdf, formato)
85
- return resultado, zip_path
86
-
87
- # ------------------------------------------------------------
88
- # ZIP utilitário (para um único PDF) — usado nos helpers acima
89
- # ------------------------------------------------------------
90
- def _salvar_e_zipar(
91
- imagens: List[Tuple[int, io.BytesIO]],
92
- nome_pdf: str,
93
- formato: str
94
- ) -> str:
95
- temp_dir = tempfile.mkdtemp()
96
- temp_path = Path(temp_dir)
97
- ext = "ico" if formato == "ico" else formato.lower()
98
-
99
- for pagina, img_io in imagens:
100
- filename = f"{nome_pdf}_pagina_{pagina}.{ext}"
101
- (temp_path / filename).write_bytes(img_io.getvalue())
102
- img_io.seek(0)
103
-
104
- base_name = Path.cwd() / nome_pdf # zip ficará como <nome_pdf>.zip
105
- zip_path = shutil.make_archive(str(base_name), "zip", temp_dir)
106
- return zip_path
107
-
108
- # ------------------------------------------------------------
109
- # Coleta PDFs a partir de paths “mistos”: PDFs diretos e PDFs dentro de .zip
110
- # ------------------------------------------------------------
111
- def _coletar_pdfs(paths: List[Path]) -> List[Tuple[str, bytes]]:
112
- """
113
- Recebe caminhos de arquivos enviados (podem ser PDFs ou .zip).
114
- Retorna uma lista de tuplas (nome_base_pdf, pdf_bytes) para cada PDF encontrado.
115
- - Se for .zip, itera pelos itens *.pdf (case-insensitive) e lê os bytes sem extrair em disco.
116
- - Se for .pdf, lê direto do disco.
117
- """
118
- coletados: List[Tuple[str, bytes]] = []
119
- for p in paths:
120
- suf = p.suffix.lower()
121
- if suf == ".pdf":
122
- coletados.append((p.stem, p.read_bytes()))
123
- elif suf == ".zip":
124
- with zipfile.ZipFile(p, "r") as zf:
125
- for zi in zf.infolist():
126
- if zi.is_dir():
127
- continue
128
- if zi.filename.lower().endswith(".pdf"):
129
- base = Path(zi.filename).stem
130
- with zf.open(zi, "r") as f:
131
- coletados.append((base, f.read()))
132
- else:
133
- # Ignora outros tipos (p. ex., .txt) para este app
134
- continue
135
- return coletados
136
-
137
- # ------------------------------------------------------------
138
- # Pipeline “mistão”: processa 1..N PDFs (e PDFs dentro de .zip) de uma vez
139
- # ------------------------------------------------------------
140
- def processar_misto(
141
- paths: List[Path],
142
- modo: str,
143
- paginas: Optional[List[int]],
144
- formato: str
145
- ) -> Tuple[List[Tuple[str, io.BytesIO]], str]:
146
- """
147
- Processa tudo em uma passada:
148
- - Lê todos os PDFs enviados (diretos e dentro de .zip)
149
- - Converte páginas segundo 'modo' e 'paginas'
150
- - Escreve TODAS as imagens em uma mesma pasta temporária com nomes:
151
- NomeDoPDF_pagina_X.<ext>
152
- - Gera UM único ZIP com todas as imagens
153
- - Retorna:
154
- imagens_galeria -> [(legenda, BytesIO), ...]
155
- zip_path -> caminho do ZIP único
156
- """
157
- pdfs = _coletar_pdfs(paths)
158
- if not pdfs:
159
- raise RuntimeError("Nenhum PDF encontrado nos arquivos enviados.")
160
-
161
- # Pasta temporária na qual vamos gravar TUDO
162
- temp_dir = tempfile.mkdtemp()
163
- temp_path = Path(temp_dir)
164
- ext = "ico" if formato == "ico" else formato.lower()
165
-
166
- imagens_galeria: List[Tuple[str, io.BytesIO]] = []
167
-
168
- for base, pdf_bytes in pdfs:
169
- if modo == "Extrair todas as páginas":
170
- lista_paginas, _ = extrair_todas_as_paginas(pdf_bytes, base, formato)
171
- elif modo == "Extrair páginas específicas":
172
- if not paginas:
173
- raise RuntimeError("Nenhuma página válida foi informada.")
174
- lista_paginas, _ = extrair_paginas_especificas(pdf_bytes, base, paginas, formato)
175
- else:
176
- raise RuntimeError("Modo inválido.")
177
-
178
- # Grava no diretório TEMP (um único ZIP final) e prepara galeria
179
- for pagina, img_io in lista_paginas:
180
- # nome do arquivo no ZIP final
181
- filename = f"{base}_pagina_{pagina}.{ext}"
182
- (temp_path / filename).write_bytes(img_io.getvalue())
183
- img_io.seek(0)
184
-
185
- # legenda para galeria
186
- imagens_galeria.append((f"{base} — pág {pagina}", img_io))
187
-
188
- # Nome do ZIP: se for 1 PDF, usa o nome dele; senão, nome genérico com timestamp
189
- if len(pdfs) == 1:
190
- base_zip = pdfs[0][0]
191
- else:
192
- base_zip = f"imagens_extraidas_{time.strftime('%Y%m%d-%H%M%S')}"
193
-
194
- zip_path = shutil.make_archive(str(Path.cwd() / base_zip), "zip", temp_dir)
195
- return imagens_galeria, zip_path
 
1
+ # processador.py
2
+ from __future__ import annotations
3
+ from pathlib import Path
4
+ from typing import List, Optional, Tuple
5
+ import platform, shutil, tempfile, zipfile
6
+
7
+ from pdf2image import convert_from_path, convert_from_bytes
8
+
9
+ IS_WINDOWS = platform.system() == "Windows"
10
+ # No Windows você aponta abaixo; no Linux (Hugging Face) deixe como None
11
+ POPPLER_PATH = r"C:\poppler-25.07.0\Library\bin" if IS_WINDOWS else None
12
+
13
+
14
+ def _ensure_poppler() -> None:
15
+ """Garante que o Poppler está disponível no ambiente."""
16
+ if IS_WINDOWS:
17
+ p = Path(POPPLER_PATH or "")
18
+ if not p.exists():
19
+ raise RuntimeError(
20
+ "Poppler não encontrado. Ajuste POPPLER_PATH para ...\\poppler-XX\\Library\\bin"
21
+ )
22
+ else:
23
+ # No Linux (HF) esperamos 'pdftoppm' no PATH via packages.txt (poppler-utils)
24
+ if shutil.which("pdftoppm") is None:
25
+ raise RuntimeError(
26
+ "pdftoppm não encontrado. No Hugging Face inclua 'packages.txt' com 'poppler-utils'."
27
+ )
28
+
29
+
30
+ def _ext(formato: str) -> str:
31
+ """Normaliza a extensão."""
32
+ return "jpg" if formato.lower() in ("jpeg", "jpg") else formato.lower()
33
+
34
+
35
+ def _convert_bytes(data: bytes, formato: str, paginas: Optional[List[int]]):
36
+ fmt = _ext(formato)
37
+ if not paginas:
38
+ return convert_from_bytes(data, dpi=200, fmt=fmt, poppler_path=POPPLER_PATH)
39
+ # páginas específicas: chamamos 1 a 1
40
+ out = []
41
+ for p in paginas:
42
+ img = convert_from_bytes(
43
+ data, dpi=200, fmt=fmt, first_page=p, last_page=p, poppler_path=POPPLER_PATH
44
+ )[0]
45
+ out.append(img)
46
+ return out
47
+
48
+
49
+ def _convert_path(pdf_path: Path, formato: str, paginas: Optional[List[int]]):
50
+ fmt = _ext(formato)
51
+ if not paginas:
52
+ return convert_from_path(str(pdf_path), dpi=200, fmt=fmt, poppler_path=POPPLER_PATH)
53
+ out = []
54
+ for p in paginas:
55
+ img = convert_from_path(
56
+ str(pdf_path), dpi=200, fmt=fmt, first_page=p, last_page=p, poppler_path=POPPLER_PATH
57
+ )[0]
58
+ out.append(img)
59
+ return out
60
+
61
+
62
+ def processar_misto(
63
+ paths: List[Path], modo: str, paginas: Optional[List[int]], formato: str
64
+ ) -> Tuple[List[Tuple[str, str]], str]:
65
+ """
66
+ paths: lista de caminhos (PDFs ou ZIPs contendo PDFs)
67
+ modo: "Extrair todas as páginas" | "Extrair páginas específicas"
68
+ paginas: lista de ints (ou None) quando modo = específicas
69
+ formato: "jpeg" | "png" | "bmp" | "ico"
70
+ Retorna: [(caminho_imagem_temp, legenda)], caminho_zip_temp
71
+ """
72
+ _ensure_poppler()
73
+
74
+ usar_paginas = paginas if "Específicas" in modo else None
75
+ ext = _ext(formato)
76
+
77
+ galeria: List[Tuple[str, str]] = []
78
+ tmp_zip = tempfile.NamedTemporaryFile(delete=False, suffix=".zip")
79
+ with zipfile.ZipFile(tmp_zip.name, "w", compression=zipfile.ZIP_DEFLATED) as zout:
80
+ for path in paths:
81
+ if path.suffix.lower() == ".zip":
82
+ with zipfile.ZipFile(path, "r") as zin:
83
+ for info in zin.infolist():
84
+ if info.filename.lower().endswith(".pdf"):
85
+ data = zin.read(info.filename)
86
+ images = _convert_bytes(data, formato, usar_paginas)
87
+ stem_zip = Path(info.filename).stem.replace("/", "_")
88
+ for i, img in enumerate(images, 1):
89
+ img_tmp = tempfile.NamedTemporaryFile(delete=False, suffix=f".{ext}")
90
+ img.save(img_tmp.name)
91
+ galeria.append((img_tmp.name, f"{stem_zip} — p{i}"))
92
+ zout.write(img_tmp.name, arcname=f"{stem_zip}_p{i}.{ext}")
93
+ else:
94
+ images = _convert_path(path, formato, usar_paginas)
95
+ for i, img in enumerate(images, 1):
96
+ img_tmp = tempfile.NamedTemporaryFile(delete=False, suffix=f".{ext}")
97
+ img.save(img_tmp.name)
98
+ galeria.append((img_tmp.name, f"{path.stem} — p{i}"))
99
+ zout.write(img_tmp.name, arcname=f"{path.stem}_p{i}.{ext}")
100
+
101
+ return galeria, tmp_zip.name