WallaceBrasil commited on
Commit
363972c
·
verified ·
1 Parent(s): 2991cae

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +193 -194
app.py CHANGED
@@ -1,194 +1,193 @@
1
- # app.py — versão com tema/estilo padronizado
2
- import gradio as gr
3
- import tempfile
4
- from pathlib import Path
5
- from processador import processar_misto # pipeline que aceita PDF(s) e ZIP(s)
6
-
7
- # -------------------- Tema + CSS do portfólio --------------------
8
- CUSTOM_CSS = """
9
- :root{
10
- --bg:#000; /* fundo geral */
11
- --panel:#0b0b0b; /* blocos/painéis */
12
- --panel-2:#0e0e0e; /* inputs/dropdowns */
13
- --border:#2a2a2a; /* borda padrão */
14
- --text:#e5e5e5; /* texto branco suave */
15
- --muted:#a3a3a3; /* texto secundário */
16
- --accent:#6ee7b7; /* cor do foco/seleção (verde menta) */
17
- }
18
-
19
- /* fonte geral (system UI) */
20
- html, body, .gradio-container {
21
- background: var(--bg)!important;
22
- color: var(--text)!important;
23
- font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Inter, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif !important;
24
- }
25
-
26
- /* blocos/painéis */
27
- .gradio-container .block,
28
- .gradio-container .gr-box,
29
- .gradio-container .gr-panel {
30
- background: var(--panel) !important;
31
- border: 1px solid var(--border) !important;
32
- border-radius: 12px !important;
33
- }
34
-
35
- /* remover o bloco atrás do TÍTULO (qualquer .block que contenha h1) */
36
- .gradio-container .block:has(h1){
37
- background: transparent !important;
38
- border: 0 !important;
39
- box-shadow: none !important;
40
- }
41
-
42
- /* botões estilo "pílula" */
43
- button, .gr-button{
44
- border-radius: 9999px !important;
45
- border: 1px solid var(--border) !important;
46
- background: var(--panel-2) !important;
47
- }
48
- button:hover{ border-color:#4a4a4a !important; }
49
-
50
- /* inputs/textarea/file/dropdown: mesmo tom escuro */
51
- input, textarea, select,
52
- .gradio-container .gr-textbox,
53
- .gradio-container .gr-input,
54
- .gradio-container .gradio-dropdown,
55
- .gradio-container .gr-file,
56
- .gradio-container .gr-file-download,
57
- .gradio-container .gr-select-container,
58
- .gradio-container .wrap .items-center select {
59
- background: var(--panel-2) !important;
60
- border: 1px solid var(--border) !important;
61
- color: var(--text) !important;
62
- border-radius: 12px !important;
63
- }
64
-
65
- /* opções do select */
66
- select > option { background: var(--panel-2); color: var(--text); }
67
-
68
- /* foco/seleção visível (inputs, selects, file, etc.) */
69
- input:focus, textarea:focus, select:focus,
70
- .gradio-container .gr-textbox:focus-within,
71
- .gradio-container .gr-input:focus-within,
72
- .gradio-container .gradio-dropdown:focus-within,
73
- .gradio-container .gr-file:focus-within,
74
- .gradio-container .gr-select-container:focus-within {
75
- outline: none !important;
76
- border-color: var(--accent) !important;
77
- box-shadow: 0 0 0 2px rgba(110,231,183,0.18) !important; /* halo */
78
- }
79
-
80
- /* uploader QUADRADO (remove círculo/arredondamento exagerado) */
81
- .gradio-container [data-testid="file"] .rounded-full,
82
- .gradio-container [data-testid="files"] .rounded-full { border-radius:12px !important; }
83
- .gradio-container [data-testid="file"] [class*="aspect-"],
84
- .gradio-container [data-testid="files"] [class*="aspect-"] { aspect-ratio:auto !important; }
85
- .gradio-container [data-testid="file"] .h-full.w-full,
86
- .gradio-container [data-testid="files"] .h-full.w-full { border-radius:12px !important; }
87
-
88
- /* gallery e saídas de arquivo */
89
- .gradio-container .gr-gallery,
90
- .gradio-container .gr-file-download{
91
- background: var(--panel-2) !important;
92
- border: 1px solid var(--border) !important;
93
- border-radius: 12px !important;
94
- }
95
-
96
- /* badges/labels */
97
- .badge, .token { background:#0f0f0f !important; border-radius:9999px !important; }
98
-
99
- .gradio-container .fixed.bottom-0,
100
- .gradio-container div[class*="fixed"][class*="bottom-0"],
101
- .gradio-container footer,
102
- body > div.fixed.bottom-0,
103
- div.fixed.bottom-0 {
104
- display: none !important;
105
- visibility: hidden !important;
106
- height: 0 !important;
107
- overflow: hidden !important;
108
- pointer-events: none !important;
109
- }
110
- """
111
-
112
- THEME = gr.themes.Soft(primary_hue="zinc", neutral_hue="zinc")
113
-
114
- # -----------------------------------------------------------
115
-
116
- # Converte buffers em arquivos temporários para a Gallery
117
- def _galeria_temp(imagens, formato):
118
- gal = []
119
- ext = "ico" if formato == "ico" else formato.lower()
120
- for legenda, img_io in imagens:
121
- tmp = tempfile.NamedTemporaryFile(delete=False, suffix=f".{ext}")
122
- tmp.write(img_io.read())
123
- tmp.close()
124
- img_io.seek(0)
125
- gal.append((tmp.name, legenda))
126
- return gal
127
-
128
- # Função chamada pelo submit
129
- def processar(arquivos, modo, paginas_input, formato_opcao):
130
- if not arquivos:
131
- return "Por favor, envie ao menos um PDF (ou .zip contendo PDFs).", None
132
-
133
- # gr.Files pode vir string ou lista:
134
- paths = [Path(arquivos)] if isinstance(arquivos, str) else [Path(p) for p in arquivos]
135
-
136
- # Do dropdown “jpeg [Recomendado …]” pegamos só o formato:
137
- formato = formato_opcao.split(" [")[0].lower()
138
-
139
- # Interpreta páginas quando necessário
140
- paginas = None
141
- if modo == "Extrair páginas específicas":
142
- try:
143
- paginas = [int(p.strip()) for p in paginas_input.split("-") if p.strip().isdigit()]
144
- if not paginas:
145
- return "Nenhuma página válida foi informada.", None
146
- except Exception as e:
147
- return f"Erro ao interpretar as páginas: {e}", None
148
-
149
- try:
150
- imagens, zip_path = processar_misto(paths, modo, paginas, formato)
151
- except Exception as e:
152
- return f"Erro ao processar: {e}", None
153
-
154
- galeria = _galeria_temp(imagens, formato)
155
- return galeria, zip_path
156
-
157
- FORMATOS = [
158
- " jpeg [Recomendado - compacto e boa qualidade]",
159
- " png [Alta qualidade, suporta transparência]",
160
- " bmp [Sem compressão - ideal para edição bruta]",
161
- " ico [Favicon para sites, atalhos e apps]",
162
- ]
163
-
164
- demo = gr.Interface(
165
- fn=processar,
166
- inputs=[
167
- gr.Files(
168
- label="Envie PDF(s) ou .zip com PDFs",
169
- file_count="multiple",
170
- file_types=[".pdf", ".zip"],
171
- ),
172
- gr.Radio(
173
- ["Extrair todas as páginas", "Extrair páginas específicas"],
174
- label="Modo",
175
- value="Extrair todas as páginas",
176
- ),
177
- gr.Textbox(
178
- label="Páginas (se usar 'Específicas')",
179
- placeholder="Ex.: 3 - 5 - 10",
180
- ),
181
- gr.Dropdown(choices=FORMATOS, value=FORMATOS[0], label="Formato das imagens"),
182
- ],
183
- outputs=[
184
- gr.Gallery(label="Imagens"),
185
- gr.File(label="ZIP com as imagens"),
186
- ],
187
- title="Extrator de Imagens de PDF",
188
- allow_flagging="never",
189
- theme=THEME,
190
- css=CUSTOM_CSS,
191
- )
192
-
193
- if __name__ == "__main__":
194
- demo.launch()
 
1
+ # app.py — versão final p/ Hugging Face (tema + UI padronizados)
2
+ import gradio as gr
3
+ import re
4
+ from pathlib import Path
5
+ from typing import List
6
+ from processador import processar_misto # devolve [(caminho_img, legenda)], caminho_zip
7
+
8
+ # -------------------- Tema + CSS do portfólio --------------------
9
+ CUSTOM_CSS = """
10
+ :root{
11
+ --bg:#000; /* fundo geral */
12
+ --panel:#0b0b0b; /* blocos/painéis */
13
+ --panel-2:#0e0e0e; /* inputs/dropdowns */
14
+ --border:#2a2a2a; /* borda padrão */
15
+ --text:#e5e5e5; /* texto branco suave */
16
+ --muted:#a3a3a3; /* texto secundário */
17
+ --accent:#6ee7b7; /* cor do foco/seleção (verde menta) */
18
+ }
19
+
20
+ /* fonte geral (system UI) */
21
+ html, body, .gradio-container {
22
+ background: var(--bg)!important;
23
+ color: var(--text)!important;
24
+ font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Inter, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif !important;
25
+ }
26
+
27
+ /* blocos/painéis */
28
+ .gradio-container .block,
29
+ .gradio-container .gr-box,
30
+ .gradio-container .gr-panel {
31
+ background: var(--panel) !important;
32
+ border: 1px solid var(--border) !important;
33
+ border-radius: 12px !important;
34
+ }
35
+
36
+ /* remover o bloco atrás do TÍTULO */
37
+ .gradio-container .block:has(h1){
38
+ background: transparent !important;
39
+ border: 0 !important;
40
+ box-shadow: none !important;
41
+ }
42
+
43
+ /* botões estilo "pílula" */
44
+ button, .gr-button{
45
+ border-radius: 9999px !important;
46
+ border: 1px solid var(--border) !important;
47
+ background: var(--panel-2) !important;
48
+ }
49
+ button:hover{ border-color:#4a4a4a !important; }
50
+
51
+ /* inputs/textarea/file/dropdown */
52
+ input, textarea, select,
53
+ .gradio-container .gr-textbox,
54
+ .gradio-container .gr-input,
55
+ .gradio-container .gradio-dropdown,
56
+ .gradio-container .gr-file,
57
+ .gradio-container .gr-file-download,
58
+ .gradio-container .gr-select-container,
59
+ .gradio-container .wrap .items-center select {
60
+ background: var(--panel-2) !important;
61
+ border: 1px solid var(--border) !important;
62
+ color: var(--text) !important;
63
+ border-radius: 12px !important;
64
+ }
65
+ select > option { background: var(--panel-2); color: var(--text); }
66
+
67
+ /* foco/seleção visível */
68
+ input:focus, textarea:focus, select:focus,
69
+ .gradio-container .gr-textbox:focus-within,
70
+ .gradio-container .gr-input:focus-within,
71
+ .gradio-container .gradio-dropdown:focus-within,
72
+ .gradio-container .gr-file:focus-within,
73
+ .gradio-container .gr-select-container:focus-within {
74
+ outline: none !important;
75
+ border-color: var(--accent) !important;
76
+ box-shadow: 0 0 0 2px rgba(110,231,183,0.18) !important;
77
+ }
78
+
79
+ /* uploader QUADRADO */
80
+ .gradio-container [data-testid="file"] .rounded-full,
81
+ .gradio-container [data-testid="files"] .rounded-full { border-radius:12px !important; }
82
+ .gradio-container [data-testid="file"] [class*="aspect-"],
83
+ .gradio-container [data-testid="files"] [class*="aspect-"] { aspect-ratio:auto !important; }
84
+ .gradio-container [data-testid="file"] .h-full.w-full,
85
+ .gradio-container [data-testid="files"] .h-full.w-full { border-radius:12px !important; }
86
+
87
+ /* gallery e saídas */
88
+ .gradio-container .gr-gallery,
89
+ .gradio-container .gr-file-download{
90
+ background: var(--panel-2) !important;
91
+ border: 1px solid var(--border) !important;
92
+ border-radius: 12px !important;
93
+ }
94
+
95
+ /* badges/labels */
96
+ .badge, .token { background:#0f0f0f !important; border-radius:9999px !important; }
97
+
98
+ /* esconder rodapé do Gradio */
99
+ .gradio-container .fixed.bottom-0,
100
+ .gradio-container div[class*="fixed"][class*="bottom-0"],
101
+ .gradio-container footer,
102
+ body > div.fixed.bottom-0,
103
+ div.fixed.bottom-0 {
104
+ display: none !important;
105
+ visibility: hidden !important;
106
+ height: 0 !important;
107
+ overflow: hidden !important;
108
+ pointer-events: none !important;
109
+ }
110
+ """
111
+
112
+ THEME = gr.themes.Soft(primary_hue="zinc", neutral_hue="zinc")
113
+
114
+ # -------------------- Util --------------------
115
+ def _normalize_paths(arquivos) -> List[Path]:
116
+ """gr.Files pode entregar str, Path, objeto com .name ou até dict-like."""
117
+ items = arquivos if isinstance(arquivos, list) else [arquivos]
118
+ paths: List[Path] = []
119
+ for a in items:
120
+ if isinstance(a, (str, Path)):
121
+ paths.append(Path(a))
122
+ else:
123
+ name = getattr(a, "name", None)
124
+ if name is None:
125
+ try:
126
+ name = a["name"] # alguns retornam dict-like
127
+ except Exception:
128
+ name = str(a)
129
+ paths.append(Path(name))
130
+ return paths
131
+
132
+ # -------------------- Função do app --------------------
133
+ def processar(arquivos, modo, paginas_input, formato_opcao):
134
+ if not arquivos:
135
+ return "Por favor, envie ao menos um PDF (ou .zip com PDFs).", None
136
+
137
+ paths = _normalize_paths(arquivos)
138
+
139
+ # pega "jpeg" de "jpeg [Recomendado - ...]"
140
+ formato = formato_opcao.split(" [")[0].strip().lower()
141
+
142
+ paginas = None
143
+ if "específica" in modo.lower(): # aceita "Específicas"/"Específica"
144
+ nums = re.findall(r"\d+", paginas_input or "")
145
+ if not nums:
146
+ return "Nenhuma página válida foi informada.", None
147
+ paginas = [int(n) for n in nums]
148
+
149
+ try:
150
+ galeria, zip_path = processar_misto(paths, modo, paginas, formato)
151
+ return galeria, zip_path # Gallery aceita lista de (caminho, legenda)
152
+ except Exception as e:
153
+ return f"Erro ao processar: {e}", None
154
+
155
+ # opções do dropdown (sem espaços no início)
156
+ FORMATOS = [
157
+ "jpeg [Recomendado - compacto e boa qualidade]",
158
+ "png [Alta qualidade, suporta transparência]",
159
+ "bmp [Sem compressão - ideal para edição bruta]",
160
+ "ico [Favicon para sites, atalhos e apps]",
161
+ ]
162
+
163
+ demo = gr.Interface(
164
+ fn=processar,
165
+ inputs=[
166
+ gr.Files(
167
+ label="Envie PDF(s) ou .zip com PDFs",
168
+ file_count="multiple",
169
+ file_types=[".pdf", ".zip"],
170
+ ),
171
+ gr.Radio(
172
+ ["Extrair todas as páginas", "Extrair páginas específicas"],
173
+ label="Modo",
174
+ value="Extrair todas as páginas",
175
+ ),
176
+ gr.Textbox(
177
+ label="Páginas (se usar 'Específicas')",
178
+ placeholder="Ex.: 3 - 5 - 10",
179
+ ),
180
+ gr.Dropdown(choices=FORMATOS, value=FORMATOS[0], label="Formato das imagens"),
181
+ ],
182
+ outputs=[
183
+ gr.Gallery(label="Imagens"),
184
+ gr.File(label="ZIP com as imagens"),
185
+ ],
186
+ title="Extrator de Imagens de PDF",
187
+ allow_flagging="never",
188
+ theme=THEME,
189
+ css=CUSTOM_CSS,
190
+ )
191
+
192
+ if __name__ == "__main__":
193
+ demo.launch()