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