feat: add SVG asset management, icons, and UI component enhancements
@@ -1,6 +1,6 @@
|
||||
# Pasta de Ícones (SVG) e Imagens (PNG) — CyberCore Signer
|
||||
# Recursos Visuais (Ícones e Imagens em SVG) — CyberCore Signer
|
||||
|
||||
Esta pasta é o repositório oficial onde você pode visualizar, customizar e substituir livremente os **ícones em SVG** e as **imagens em PNG** do **CyberCore Signer**.
|
||||
Esta pasta é o repositório oficial onde você pode visualizar, customizar e substituir livremente todos os **ícones em SVG** e as **imagens em SVG** do **CyberCore Signer**.
|
||||
|
||||
---
|
||||
|
||||
@@ -31,13 +31,23 @@ assets/
|
||||
│ ├── seal.svg # Ícone Designer e Gerenciador de Selos Notariais
|
||||
│ ├── check.svg # Ícone Checkmark de sucesso e validação
|
||||
│ ├── error.svg # Ícone de Erro / Alerta em vermelho
|
||||
│ └── star.svg # Ícone de Estrela notarial / favorita
|
||||
│ ├── star.svg # Ícone de Estrela notarial / favorita
|
||||
│ ├── close.svg # Ícone Fechar / Cancelar (cruz geométrica moderna)
|
||||
│ ├── arrow-right.svg # Ícone Avançar / Seta direcional
|
||||
│ ├── zoom-in.svg # Ícone Zoom In (+)
|
||||
│ ├── zoom-out.svg # Ícone Zoom Out (-)
|
||||
│ ├── zoom-fit.svg # Ícone Ajustar à Tela
|
||||
│ └── refresh.svg # Ícone Atualizar / Recarregar listas e status
|
||||
│
|
||||
└── images/ # Imagens de alta fidelidade em PNG com canal alfa (transparência)
|
||||
├── app-icon.png # Ícone oficial do app (256x256 px - Diamante 3D azul Serpro)
|
||||
├── app-logo.png # Logotipo horizontal oficial (480x120 px - CyberCore SIGNER)
|
||||
├── brand-banner.png # Banner de cabeçalho e marca (600x160 px - Serpro Blue)
|
||||
└── default-seal.png # Selo de assinatura padrão ICP-Brasil (640x164 px)
|
||||
└── images/ # Imagens vetoriais SVG editáveis (com cópias PNG de fallback)
|
||||
├── app-icon.svg # Ícone oficial do app em SVG (Cristal/Diamante 3D azul)
|
||||
├── app-icon.png # Fallback em PNG (256x256 px)
|
||||
├── app-logo.svg # Logotipo horizontal oficial em SVG (CyberCore SIGNER)
|
||||
├── app-logo.png # Fallback em PNG (480x120 px)
|
||||
├── brand-banner.svg # Banner de cabeçalho e marca em SVG (600x160 px)
|
||||
├── brand-banner.png # Fallback em PNG (600x160 px)
|
||||
├── default-seal.svg # Selo de assinatura padrão ICP-Brasil em SVG (640x164 px)
|
||||
└── default-seal.png # Fallback em PNG (640x164 px)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -45,20 +55,21 @@ assets/
|
||||
## 🎨 Como Customizar Qualquer Ícone ou Imagem
|
||||
|
||||
1. **Ícones:**
|
||||
- Estão no formato `.svg` padrão.
|
||||
- Podem ser editados diretamente no seu editor de código (VS Code, etc.) ou em editores vetoriais como **Inkscape, Figma ou Illustrator**.
|
||||
- Se preferir colocar uma imagem bitmap para um ícone específico, você também pode colocar um `.png` (ex: `pdf.png`). O sistema prioriza `.svg` e aceita `.png` com fallback transparente.
|
||||
- Todos estão no formato `.svg` padrão da W3C.
|
||||
- Podem ser abertos e editados diretamente em editores de texto/código (VS Code) ou ferramentas vetoriais como **Inkscape, Figma, Adobe Illustrator**.
|
||||
- Se preferir usar um arquivo bitmap para algum ícone específico, basta salvar um `.png` correspondente na pasta (ex: `pdf.png`). O sistema detecta e renderiza automaticamente.
|
||||
|
||||
2. **Imagens do Sistema:**
|
||||
- Estão no formato `.png` com canal alfa de 32 bits (fundo transparente).
|
||||
- Você pode substituir `app-logo.png`, `app-icon.png`, `brand-banner.png` ou `default-seal.png` pelo logotipo e identidade visual da sua instituição ou empresa.
|
||||
- Tanto o logotipo horizontal (`app-logo.svg`), o ícone do sistema (`app-icon.svg`), o banner institucional (`brand-banner.svg`) e o selo notarial padrão (`default-seal.svg`) agora são arquivos **SVG vetoriais editáveis**.
|
||||
- Você pode alterar cores, tipografias, brasões e dimensões diretamente nos arquivos SVG.
|
||||
- O `AssetManager` renderiza os arquivos SVG preservando fidelidade geométrica e suportando gradientes e alinhamentos.
|
||||
|
||||
3. **Ordem de Prioridade e Hot-Reloading:**
|
||||
Quando a aplicação busca um ícone ou imagem, a resolução ocorre na ordem:
|
||||
1. **Pasta Física Local (`./assets/icons/` e `./assets/images/`)**: Qualquer alteração feita aqui entra em vigor imediatamente ao abrir a janela ou reiniciar a aplicação, sem necessidade de recompilar!
|
||||
2. **Pasta do Usuário (`~/.cybercore/assets/`)**: Configurações específicas por usuário do sistema operacional.
|
||||
3. **Resources Empacotados no JAR (`/assets/`)**: Cópia empacotada internamente no `.jar`.
|
||||
4. **Fallback Vetorial Nativo (`SvgIcons`)**: Garantia de que a interface nunca fique em branco caso algum arquivo seja excluído.
|
||||
3. **Ordem de Resolução e Carregamento:**
|
||||
Ao requisitar qualquer elemento gráfico, a aplicação segue a ordem de prioridade:
|
||||
1. **Pasta Física Local (`./assets/icons/` e `./assets/images/`)**: Arquivos `.svg` editados aqui têm prioridade máxima e entram em vigor imediatamente sem necessidade de recompilar.
|
||||
2. **Pasta do Usuário (`~/.cybercore/assets/`)**: Para customizações no diretório pessoal do usuário.
|
||||
3. **Recursos Empacotados no JAR (`/assets/`)**: SVGs e imagens internas empacotadas no pacote de distribuição.
|
||||
4. **Fallback Vetorial Nativo (`SvgIcons`)**: Garantia de que a interface nunca fique desprovida de grafismos caso algum arquivo seja removido.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16"><title>info-circle-fill</title><path fill="currentColor" d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.93-9.412l-1 4.705c-.07.34.029.533.304.533c.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598c-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081l.082-.381l2.29-.287zM8 5.5a1 1 0 1 1 0-2a1 1 0 0 1 0 2"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<!-- Círculo azul e letra 'i' informativa centrada em (24, 24) -->
|
||||
<circle cx="24" cy="24" r="18" fill="#0288d1"/>
|
||||
<circle cx="24" cy="15" r="2.5" fill="#ffffff"/>
|
||||
<line x1="24" y1="21" x2="24" y2="33" stroke="#ffffff" stroke-width="4.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 429 B After Width: | Height: | Size: 362 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path d="M 8 5 L 15 12 L 8 19" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 221 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path d="M 6 6 L 18 18 M 18 6 L 6 18" fill="none" stroke="#64748b" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 228 B |
@@ -1,6 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<!-- Lua crescente ciano/índigo -->
|
||||
<path d="M 40 26.5 A 18 18 0 1 1 21.5 8 A 14 14 0 0 0 40 26.5 Z" fill="#38bdf8"/>
|
||||
<circle cx="36" cy="12" r="1.5" fill="#818cf8"/>
|
||||
<circle cx="42" cy="18" r="1" fill="#818cf8"/>
|
||||
<!-- Lua crescente ciano/índigo perfeitamente centrada em (24, 24) -->
|
||||
<path d="M 24 8 A 16 16 0 1 0 40 24 A 14 14 0 0 1 24 8 Z" fill="#38bdf8"/>
|
||||
<circle cx="34" cy="14" r="1.8" fill="#818cf8"/>
|
||||
<circle cx="38" cy="22" r="1.2" fill="#818cf8"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 314 B After Width: | Height: | Size: 344 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path d="M 20 4 L 20 9 L 15 9" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M 4 20 L 4 15 L 9 15" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M 5.8 9 A 7 7 0 0 1 18.5 7.5 L 20 9 M 4 15 L 5.5 16.5 A 7 7 0 0 0 18.2 15" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 510 B |
@@ -1,8 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<!-- Engrenagem simétrica de 8 dentes centrada em (24, 24) -->
|
||||
<path d="M 21 4 L 27 4 L 28 10 C 30 10.8 31.8 11.8 33.4 13.2 L 39 10 L 43 14 L 39.8 19.6 C 41.2 21.2 42.2 23 43 25 L 49 26 L 49 32 L 43 33 C 42.2 35 41.2 36.8 39.8 38.4 L 43 44 L 39 48 L 33.4 44.8 C 31.8 46.2 30 47.2 28 48 L 27 54 L 21 54 L 20 48 C 18 47.2 16.2 46.2 14.6 44.8 L 9 48 L 5 44 L 8.2 38.4 C 6.8 36.8 5.8 35 5 33 L -1 32 L -1 26 L 5 25 C 5.8 23 6.8 21.2 8.2 19.6 L 5 14 L 9 10 L 14.6 13.2 C 16.2 11.8 18 10.8 20 10 Z" transform="scale(0.8) translate(6, 4)" fill="#0288d1"/>
|
||||
<!-- Círculo externo da engrenagem -->
|
||||
<circle cx="24" cy="24" r="14" fill="#0288d1"/>
|
||||
<!-- Furo central vazado -->
|
||||
<circle cx="24" cy="24" r="6" fill="#ffffff"/>
|
||||
<!-- Engrenagem corporativa simétrica centrada em (24, 24) -->
|
||||
<path fill="#0288d1" fill-rule="evenodd" d="M 21.5 5 L 26.5 5 L 27.2 10.2 C 29 10.9 30.7 11.9 32.2 13.1 L 36.6 10.6 L 40.1 14.1 L 37.6 18.5 C 38.8 20 39.8 21.7 40.5 23.5 L 45.7 24.2 L 45.7 29.2 L 40.5 29.9 C 39.8 31.7 38.8 33.4 37.6 34.9 L 40.1 39.3 L 36.6 42.8 L 32.2 40.3 C 30.7 41.5 29 42.5 27.2 43.2 L 26.5 48.4 L 21.5 48.4 L 20.8 43.2 C 19 42.5 17.3 41.5 15.8 40.3 L 11.4 42.8 L 7.9 39.3 L 10.4 34.9 C 9.2 33.4 8.2 31.7 7.5 29.9 L 2.3 29.2 L 2.3 24.2 L 7.5 23.5 C 8.2 21.7 9.2 20 10.4 18.5 L 7.9 14.1 L 11.4 10.6 L 15.8 13.1 C 17.3 11.9 19 10.9 20.8 10.2 Z M 24 17.5 A 6.5 6.5 0 1 0 24 30.5 A 6.5 6.5 0 1 0 24 17.5 Z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 817 B After Width: | Height: | Size: 784 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path d="M 4 9 L 4 4 L 9 4 M 15 4 L 20 4 L 20 9 M 20 15 L 20 20 L 15 20 M 9 20 L 4 20 L 4 15" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 284 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<circle cx="10" cy="10" r="7" fill="none" stroke="#0284c7" stroke-width="2.2"/>
|
||||
<path d="M 15 15 L 21 21" fill="none" stroke="#0284c7" stroke-width="2.5" stroke-linecap="round"/>
|
||||
<path d="M 10 7 L 10 13 M 7 10 L 13 10" fill="none" stroke="#0284c7" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 387 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<circle cx="10" cy="10" r="7" fill="none" stroke="#0284c7" stroke-width="2.2"/>
|
||||
<path d="M 15 15 L 21 21" fill="none" stroke="#0284c7" stroke-width="2.5" stroke-linecap="round"/>
|
||||
<path d="M 7 10 L 13 10" fill="none" stroke="#0284c7" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 372 B |
@@ -0,0 +1,37 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256">
|
||||
<defs>
|
||||
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#1976d2"/>
|
||||
<stop offset="45%" stop-color="#0d47a1"/>
|
||||
<stop offset="100%" stop-color="#01579b"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="topFacetGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.9"/>
|
||||
<stop offset="100%" stop-color="#80d8ff" stop-opacity="0.55"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="leftFacetGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#40c4ff" stop-opacity="0.75"/>
|
||||
<stop offset="100%" stop-color="#0288d1" stop-opacity="0.35"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="rightFacetGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#e1f5fe" stop-opacity="0.85"/>
|
||||
<stop offset="100%" stop-color="#29b6f6" stop-opacity="0.45"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="centerGlow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.95"/>
|
||||
<stop offset="100%" stop-color="#ffffff" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Fundo com cantos arredondados -->
|
||||
<rect x="8" y="8" width="240" height="240" rx="68" ry="68" fill="url(#bgGrad)"/>
|
||||
<rect x="10" y="10" width="236" height="236" rx="66" ry="66" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-opacity="0.25"/>
|
||||
|
||||
<!-- Facetas 3D do Cristal / Diamante Azul -->
|
||||
<polygon points="128,46 210,113 128,143 46,113" fill="url(#topFacetGrad)"/>
|
||||
<polygon points="46,113 128,143 128,215 72,179" fill="url(#leftFacetGrad)"/>
|
||||
<polygon points="210,113 128,143 128,215 184,179" fill="url(#rightFacetGrad)"/>
|
||||
|
||||
<!-- Brilho estelar central -->
|
||||
<circle cx="128" cy="128" r="38" fill="url(#centerGlow)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,41 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 120" width="480" height="120">
|
||||
<defs>
|
||||
<linearGradient id="logoIconBg" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#1976d2"/>
|
||||
<stop offset="50%" stop-color="#0d47a1"/>
|
||||
<stop offset="100%" stop-color="#01579b"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="logoTopFacet" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.95"/>
|
||||
<stop offset="100%" stop-color="#80d8ff" stop-opacity="0.6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="logoLeftFacet" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#40c4ff" stop-opacity="0.8"/>
|
||||
<stop offset="100%" stop-color="#0288d1" stop-opacity="0.4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="logoRightFacet" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#e1f5fe" stop-opacity="0.9"/>
|
||||
<stop offset="100%" stop-color="#29b6f6" stop-opacity="0.5"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Container do Ícone Cristal 3D -->
|
||||
<rect x="14" y="16" width="88" height="88" rx="24" ry="24" fill="url(#logoIconBg)"/>
|
||||
<rect x="16" y="18" width="84" height="84" rx="22" ry="22" fill="none" stroke="#ffffff" stroke-width="1.5" stroke-opacity="0.25"/>
|
||||
|
||||
<!-- Facetas Cristalinas (cx=58, cy=60, sz=63) -->
|
||||
<polygon points="58,32 86,55 58,66 30,55" fill="url(#logoTopFacet)"/>
|
||||
<polygon points="30,55 58,66 58,92 39,79" fill="url(#logoLeftFacet)"/>
|
||||
<polygon points="86,55 58,66 58,92 77,79" fill="url(#logoRightFacet)"/>
|
||||
<circle cx="58" cy="60" r="14" fill="#ffffff" fill-opacity="0.3"/>
|
||||
|
||||
<!-- Tipografia Oficial "CyberCore" -->
|
||||
<text x="124" y="62" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="42" font-weight="bold" fill="#0a2540">Cyber<tspan fill="#0288d1">Core</tspan></text>
|
||||
|
||||
<!-- Badge "SIGNER" -->
|
||||
<rect x="124" y="76" width="94" height="22" rx="6" ry="6" fill="#0288d1"/>
|
||||
<text x="139" y="92" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="13" font-weight="bold" fill="#ffffff" letter-spacing="1">SIGNER</text>
|
||||
|
||||
<!-- Subtítulo ICP-Brasil -->
|
||||
<text x="230" y="92" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#64748b">ICP-Brasil • Assinador Digital Seguro</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,32 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 160" width="600" height="160">
|
||||
<defs>
|
||||
<linearGradient id="bannerBgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#0d47a1"/>
|
||||
<stop offset="60%" stop-color="#0284c7"/>
|
||||
<stop offset="100%" stop-color="#0369a1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="bannerIconGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#1e88e5"/>
|
||||
<stop offset="100%" stop-color="#01579b"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Fundo do Banner -->
|
||||
<rect x="0" y="0" width="600" height="160" rx="16" ry="16" fill="url(#bannerBgGrad)"/>
|
||||
|
||||
<!-- Formas geométricas de segurança no fundo -->
|
||||
<circle cx="480" cy="50" r="110" fill="#ffffff" fill-opacity="0.08"/>
|
||||
<circle cx="540" cy="120" r="80" fill="#ffffff" fill-opacity="0.06"/>
|
||||
<circle cx="380" cy="-20" r="90" fill="#ffffff" fill-opacity="0.04"/>
|
||||
|
||||
<!-- Ícone Cristal à esquerda -->
|
||||
<rect x="30" y="35" width="90" height="90" rx="22" ry="22" fill="url(#bannerIconGrad)"/>
|
||||
<rect x="33" y="38" width="84" height="84" rx="19" ry="19" fill="none" stroke="#ffffff" stroke-width="2" stroke-opacity="0.3"/>
|
||||
<polygon points="75,52 101,80 75,108 49,80" fill="#ffffff" fill-opacity="0.9"/>
|
||||
<polygon points="75,52 75,108 49,80" fill="#80d8ff" fill-opacity="0.5"/>
|
||||
|
||||
<!-- Textos do Banner -->
|
||||
<text x="148" y="74" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="34" font-weight="bold" fill="#ffffff">CyberCore Signer</text>
|
||||
<text x="148" y="104" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="16" font-weight="normal" fill="#e0f2fe">Agente de Assinatura Digital ICP-Brasil A1 & A3</text>
|
||||
<text x="148" y="128" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="12" font-weight="bold" fill="#38bdf8" letter-spacing="1">PADES • XADES • CADES • TIMESTAMPS • CCSP/1</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,34 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 164" width="640" height="164">
|
||||
<defs>
|
||||
<linearGradient id="badgeGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#0288d1"/>
|
||||
<stop offset="100%" stop-color="#01579b"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Fundo do Selo -->
|
||||
<rect x="2" y="2" width="636" height="160" rx="12" ry="12" fill="#f8fafc" stroke="#0288d1" stroke-width="2.5"/>
|
||||
|
||||
<!-- Brasão Notarial no lado esquerdo -->
|
||||
<rect x="18" y="16" width="130" height="132" rx="10" ry="10" fill="url(#badgeGrad)"/>
|
||||
|
||||
<!-- Textos do Brasão -->
|
||||
<text x="83" y="44" font-family="'Segoe UI', Roboto, sans-serif" font-size="12" font-weight="bold" fill="#ffffff" text-anchor="middle" letter-spacing="0.5">ASSINADO</text>
|
||||
<text x="83" y="60" font-family="'Segoe UI', Roboto, sans-serif" font-size="11" font-weight="bold" fill="#ffffff" text-anchor="middle" letter-spacing="0.5">DIGITALMENTE</text>
|
||||
|
||||
<!-- Estrela dourada -->
|
||||
<circle cx="83" cy="85" r="14" fill="#fbbf24"/>
|
||||
<text x="83" y="91" font-family="'Segoe UI', sans-serif" font-size="16" font-weight="bold" fill="#ffffff" text-anchor="middle">★</text>
|
||||
|
||||
<text x="83" y="126" font-family="'Segoe UI', Roboto, sans-serif" font-size="10" font-weight="bold" fill="#e0f2fe" text-anchor="middle" letter-spacing="1">ICP-BRASIL</text>
|
||||
|
||||
<!-- Linha divisória vertical -->
|
||||
<line x1="166" y1="16" x2="166" y2="148" stroke="#cfd8dc" stroke-width="1.5"/>
|
||||
|
||||
<!-- Informações da Assinatura -->
|
||||
<text x="184" y="36" font-family="'Segoe UI', Roboto, sans-serif" font-size="11" font-weight="bold" fill="#64748b" letter-spacing="0.5">DOCUMENTO ASSINADO DIGITALMENTE POR:</text>
|
||||
<text x="184" y="60" font-family="'Segoe UI', Roboto, sans-serif" font-size="17" font-weight="bold" fill="#0a2540">NOME DO TITULAR DO CERTIFICADO</text>
|
||||
<text x="184" y="82" font-family="'Segoe UI', Roboto, sans-serif" font-size="12" font-weight="normal" fill="#334155">CPF: ***.000.000-**</text>
|
||||
<text x="184" y="102" font-family="'Segoe UI', Roboto, sans-serif" font-size="12" font-weight="normal" fill="#334155">Data e Hora: 11/10/2026 12:00:00 -03:00</text>
|
||||
<text x="184" y="128" font-family="'Segoe UI', Roboto, sans-serif" font-size="11" font-style="italic" fill="#0288d1">A conformidade com a ICP-Brasil pode ser verificada no ITI.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 63 KiB |
@@ -5,8 +5,14 @@ import javafx.scene.Group;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.CycleMethod;
|
||||
import javafx.scene.paint.LinearGradient;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.scene.paint.RadialGradient;
|
||||
import javafx.scene.paint.Stop;
|
||||
import javafx.scene.shape.Circle;
|
||||
import javafx.scene.shape.Line;
|
||||
import javafx.scene.shape.Polygon;
|
||||
@@ -17,7 +23,6 @@ import javafx.scene.shape.Shape;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.FontWeight;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.scene.transform.Scale;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
@@ -26,19 +31,20 @@ import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Gerenciador centralizado de recursos visuais (ícones e imagens).
|
||||
* Permite que o usuário customize ou substitua facilmente qualquer ícone (SVG/PNG) ou imagem (PNG/JPG)
|
||||
* Permite que o usuário customize ou substitua facilmente qualquer ícone ou imagem (SVG/PNG)
|
||||
* do CyberCore Signer simplesmente colocando os arquivos na pasta 'assets/'
|
||||
* na raiz do projeto ou no diretório ~/.cybercore/assets/.
|
||||
*/
|
||||
@@ -121,7 +127,7 @@ public final class AssetManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna um Node de ícone customizado (SVG ou PNG) ou invoca o fallback caso não exista imagem customizada.
|
||||
* Retorna um Node de ícone customizado (prioritariamente SVG, com fallback para PNG ou supplier vetorial).
|
||||
*/
|
||||
public static Node getIconOrDefault(String iconName, double size, Supplier<Node> fallbackSupplier) {
|
||||
if (iconName != null && !iconName.isBlank()) {
|
||||
@@ -140,7 +146,20 @@ public final class AssetManager {
|
||||
return userSvg.get();
|
||||
}
|
||||
|
||||
// 3. Se não houver SVG, tenta imagem bitmap (.png, .jpg) nas pastas externas
|
||||
// 3. Procura SVG no Classpath interno
|
||||
String resSvgPath = "/assets/icons/" + cleanName + ".svg";
|
||||
InputStream resStream = AssetManager.class.getResourceAsStream(resSvgPath);
|
||||
if (resStream != null) {
|
||||
try (resStream) {
|
||||
Node node = parseSvgToNode(resStream, size, size);
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Se não houver SVG, tenta imagem bitmap (.png, .jpg)
|
||||
Optional<Image> customImg = loadImage("icons", cleanName, size, size);
|
||||
if (customImg.isPresent() && !customImg.get().isError()) {
|
||||
ImageView iv = new ImageView(customImg.get());
|
||||
@@ -155,28 +174,76 @@ public final class AssetManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna a imagem do logotipo do sistema ou o fallback vetorial.
|
||||
* Retorna uma imagem institucional ou elemento gráfico (prioritariamente SVG, com fallback para PNG ou supplier).
|
||||
*/
|
||||
public static Node getLogoOrDefault(double width, double height, Supplier<Node> fallbackSupplier) {
|
||||
// Tenta carregar app-logo bitmap (.png) primeiro
|
||||
Optional<Image> customLogo = loadImage("images", "app-logo", width, height);
|
||||
if (customLogo.isPresent() && !customLogo.get().isError()) {
|
||||
ImageView iv = new ImageView(customLogo.get());
|
||||
public static Node getImageOrDefault(String imageName, double width, double height, Supplier<Node> fallbackSupplier) {
|
||||
if (imageName != null && !imageName.isBlank()) {
|
||||
String cleanName = cleanBaseName(imageName);
|
||||
|
||||
// 1. Prioridade para SVG em ./assets/images/
|
||||
Path localSvg = Path.of("assets", "images", cleanName + ".svg");
|
||||
if (Files.isRegularFile(localSvg)) {
|
||||
try (InputStream in = new FileInputStream(localSvg.toFile())) {
|
||||
Node node = parseSvgToNode(in, width, height);
|
||||
if (node != null) {
|
||||
logger.debug("Carregada imagem SVG local: {}", localSvg);
|
||||
return node;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Falha ao ler imagem SVG local {}: {}", localSvg, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Procura SVG em ~/.cybercore/assets/images/
|
||||
String userHome = System.getProperty("user.home", ".");
|
||||
Path userSvg = Path.of(userHome, ".cybercore", "assets", "images", cleanName + ".svg");
|
||||
if (Files.isRegularFile(userSvg)) {
|
||||
try (InputStream in = new FileInputStream(userSvg.toFile())) {
|
||||
Node node = parseSvgToNode(in, width, height);
|
||||
if (node != null) {
|
||||
logger.debug("Carregada imagem SVG do usuário: {}", userSvg);
|
||||
return node;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Falha ao ler imagem SVG do usuário {}: {}", userSvg, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Procura SVG no Classpath interno
|
||||
String resPath = "/assets/images/" + cleanName + ".svg";
|
||||
InputStream resStream = AssetManager.class.getResourceAsStream(resPath);
|
||||
if (resStream != null) {
|
||||
try (resStream) {
|
||||
Node node = parseSvgToNode(resStream, width, height);
|
||||
if (node != null) {
|
||||
return node;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Falha ao ler recurso interno SVG {}: {}", resPath, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Fallback para imagem bitmap (.png, .jpg)
|
||||
Optional<Image> customImg = loadImage("images", cleanName, width, height);
|
||||
if (customImg.isPresent() && !customImg.get().isError()) {
|
||||
ImageView iv = new ImageView(customImg.get());
|
||||
iv.setFitWidth(width);
|
||||
iv.setFitHeight(height);
|
||||
iv.setPreserveRatio(true);
|
||||
return iv;
|
||||
}
|
||||
|
||||
// Tenta carregar SVG de app-logo se existir
|
||||
Optional<Node> svgLogo = loadSvgIconFromPath(Path.of("assets", "images", "app-logo.svg"), height);
|
||||
if (svgLogo.isPresent()) {
|
||||
return svgLogo.get();
|
||||
}
|
||||
|
||||
return fallbackSupplier != null ? fallbackSupplier.get() : new ImageView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna a imagem do logotipo do sistema (SVG ou bitmap).
|
||||
*/
|
||||
public static Node getLogoOrDefault(double width, double height, Supplier<Node> fallbackSupplier) {
|
||||
return getImageOrDefault("app-logo", width, height, fallbackSupplier);
|
||||
}
|
||||
|
||||
private static String cleanBaseName(String name) {
|
||||
String clean = name;
|
||||
int dotIdx = clean.lastIndexOf('.');
|
||||
@@ -189,7 +256,7 @@ public final class AssetManager {
|
||||
private static Optional<Node> loadSvgIconFromPath(Path path, double size) {
|
||||
if (Files.isRegularFile(path)) {
|
||||
try (InputStream in = new FileInputStream(path.toFile())) {
|
||||
Node node = parseSvgToNode(in, size);
|
||||
Node node = parseSvgToNode(in, size, size);
|
||||
if (node != null) {
|
||||
logger.debug("Carregado ícone SVG customizado de: {}", path);
|
||||
return Optional.of(node);
|
||||
@@ -202,9 +269,16 @@ public final class AssetManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser analítico leve de SVG padrão para nós gráficos JavaFX com preservação geométrica exata.
|
||||
* Parser analítico de SVG para nós gráficos JavaFX com suporte a dimensões quadradas.
|
||||
*/
|
||||
public static Node parseSvgToNode(InputStream in, double targetSize) {
|
||||
return parseSvgToNode(in, targetSize, targetSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser analítico completo de SVG para nós gráficos JavaFX com dimensões proporcionais e suporte a defs/gradientes.
|
||||
*/
|
||||
public static Node parseSvgToNode(InputStream in, double targetWidth, double targetHeight) {
|
||||
try {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
dbf.setNamespaceAware(false);
|
||||
@@ -249,27 +323,34 @@ public final class AssetManager {
|
||||
if (vbWidth <= 0) vbWidth = 48;
|
||||
if (vbHeight <= 0) vbHeight = 48;
|
||||
|
||||
Group contentGroup = new Group();
|
||||
renderChildren(svg, contentGroup);
|
||||
Map<String, Paint> defsMap = new HashMap<>();
|
||||
parseDefs(svg, defsMap);
|
||||
|
||||
double maxDim = Math.max(vbWidth, vbHeight);
|
||||
double scale = (targetSize * 0.90) / maxDim;
|
||||
Group contentGroup = new Group();
|
||||
renderChildren(svg, contentGroup, defsMap);
|
||||
|
||||
double scaleX = targetWidth / vbWidth;
|
||||
double scaleY = targetHeight / vbHeight;
|
||||
double scale = Math.min(scaleX, scaleY);
|
||||
if (scale <= 0) scale = 1.0;
|
||||
|
||||
double drawnW = vbWidth * scale;
|
||||
double drawnH = vbHeight * scale;
|
||||
double tx = (targetSize - drawnW) / 2.0 - (vbMinX * scale);
|
||||
double ty = (targetSize - drawnH) / 2.0 - (vbMinY * scale);
|
||||
double tx = (targetWidth - drawnW) / 2.0 - (vbMinX * scale);
|
||||
double ty = (targetHeight - drawnH) / 2.0 - (vbMinY * scale);
|
||||
|
||||
contentGroup.setManaged(false);
|
||||
contentGroup.setLayoutX(0);
|
||||
contentGroup.setLayoutY(0);
|
||||
contentGroup.getTransforms().setAll(
|
||||
new javafx.scene.transform.Translate(tx, ty),
|
||||
new javafx.scene.transform.Scale(scale, scale, 0, 0)
|
||||
);
|
||||
|
||||
StackPane container = new StackPane(contentGroup);
|
||||
container.setPrefSize(targetSize, targetSize);
|
||||
container.setMaxSize(targetSize, targetSize);
|
||||
container.setMinSize(targetSize, targetSize);
|
||||
container.setAlignment(Pos.CENTER);
|
||||
Pane container = new Pane(contentGroup);
|
||||
container.setPrefSize(targetWidth, targetHeight);
|
||||
container.setMaxSize(targetWidth, targetHeight);
|
||||
container.setMinSize(targetWidth, targetHeight);
|
||||
return container;
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -278,12 +359,100 @@ public final class AssetManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderChildren(Element parent, Group targetGroup) {
|
||||
private static void parseDefs(Element root, Map<String, Paint> defsMap) {
|
||||
NodeList defsList = root.getElementsByTagName("defs");
|
||||
for (int i = 0; i < defsList.getLength(); i++) {
|
||||
org.w3c.dom.Node defNode = defsList.item(i);
|
||||
if (defNode instanceof Element defsEl) {
|
||||
NodeList children = defsEl.getChildNodes();
|
||||
for (int j = 0; j < children.getLength(); j++) {
|
||||
org.w3c.dom.Node c = children.item(j);
|
||||
if (c instanceof Element gradEl) {
|
||||
String id = gradEl.getAttribute("id");
|
||||
if (!id.isBlank()) {
|
||||
Paint paint = createPaintFromGradient(gradEl);
|
||||
if (paint != null) {
|
||||
defsMap.put(id, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Paint createPaintFromGradient(Element gradEl) {
|
||||
String tag = gradEl.getTagName().toLowerCase();
|
||||
List<Stop> stops = new ArrayList<>();
|
||||
NodeList stopNodes = gradEl.getElementsByTagName("stop");
|
||||
for (int i = 0; i < stopNodes.getLength(); i++) {
|
||||
org.w3c.dom.Node s = stopNodes.item(i);
|
||||
if (s instanceof Element stopEl) {
|
||||
double offset = parseOffset(stopEl.getAttribute("offset"));
|
||||
String colorStr = stopEl.getAttribute("stop-color");
|
||||
double opacity = parseDouble(stopEl.getAttribute("stop-opacity"), 1.0);
|
||||
Color baseColor = parseColor(colorStr, Color.BLACK);
|
||||
Color finalColor = new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), baseColor.getOpacity() * opacity);
|
||||
stops.add(new Stop(offset, finalColor));
|
||||
}
|
||||
}
|
||||
|
||||
if (stops.isEmpty()) {
|
||||
stops.add(new Stop(0, Color.web("#0288d1")));
|
||||
stops.add(new Stop(1, Color.web("#01579b")));
|
||||
}
|
||||
|
||||
if ("lineargradient".equals(tag)) {
|
||||
double x1 = parseCoordinate(gradEl.getAttribute("x1"), 0);
|
||||
double y1 = parseCoordinate(gradEl.getAttribute("y1"), 0);
|
||||
double x2 = parseCoordinate(gradEl.getAttribute("x2"), 1);
|
||||
double y2 = parseCoordinate(gradEl.getAttribute("y2"), 1);
|
||||
return new LinearGradient(x1, y1, x2, y2, true, CycleMethod.NO_CYCLE, stops);
|
||||
} else if ("radialgradient".equals(tag)) {
|
||||
double cx = parseCoordinate(gradEl.getAttribute("cx"), 0.5);
|
||||
double cy = parseCoordinate(gradEl.getAttribute("cy"), 0.5);
|
||||
double r = parseCoordinate(gradEl.getAttribute("r"), 0.5);
|
||||
return new RadialGradient(0, 0, cx, cy, r, true, CycleMethod.NO_CYCLE, stops);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static double parseOffset(String str) {
|
||||
if (str == null || str.isBlank()) return 0;
|
||||
String s = str.trim();
|
||||
try {
|
||||
if (s.endsWith("%")) {
|
||||
return Double.parseDouble(s.substring(0, s.length() - 1)) / 100.0;
|
||||
}
|
||||
return Double.parseDouble(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static double parseCoordinate(String str, double def) {
|
||||
if (str == null || str.isBlank()) return def;
|
||||
String s = str.trim();
|
||||
try {
|
||||
if (s.endsWith("%")) {
|
||||
return Double.parseDouble(s.substring(0, s.length() - 1)) / 100.0;
|
||||
}
|
||||
return Double.parseDouble(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderChildren(Element parent, Group targetGroup, Map<String, Paint> defsMap) {
|
||||
NodeList children = parent.getChildNodes();
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
org.w3c.dom.Node n = children.item(i);
|
||||
if (n instanceof Element el) {
|
||||
Node jfxNode = createElementNode(el);
|
||||
if ("defs".equalsIgnoreCase(el.getTagName())) {
|
||||
continue;
|
||||
}
|
||||
Node jfxNode = createElementNode(el, defsMap);
|
||||
if (jfxNode != null) {
|
||||
targetGroup.getChildren().add(jfxNode);
|
||||
}
|
||||
@@ -291,7 +460,7 @@ public final class AssetManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static Node createElementNode(Element el) {
|
||||
private static Node createElementNode(Element el, Map<String, Paint> defsMap) {
|
||||
String tagName = el.getTagName().toLowerCase();
|
||||
Shape shape = null;
|
||||
|
||||
@@ -350,12 +519,12 @@ public final class AssetManager {
|
||||
double x = parseDouble(el.getAttribute("x"), 0);
|
||||
double y = parseDouble(el.getAttribute("y"), 0);
|
||||
String content = el.getTextContent();
|
||||
Text t = new Text(x, y, content);
|
||||
double fs = parseDouble(el.getAttribute("font-size"), 10);
|
||||
Text t = new Text(x, y, content != null ? content.trim() : "");
|
||||
double fs = parseDouble(el.getAttribute("font-size"), 12);
|
||||
String fw = el.getAttribute("font-weight");
|
||||
t.setFont(Font.font("Segoe UI", "bold".equalsIgnoreCase(fw) ? FontWeight.BOLD : FontWeight.NORMAL, fs));
|
||||
if (el.hasAttribute("fill")) {
|
||||
t.setFill(parseColor(el.getAttribute("fill"), Color.BLACK));
|
||||
t.setFill(parsePaint(el.getAttribute("fill"), defsMap, Color.BLACK));
|
||||
}
|
||||
if ("middle".equalsIgnoreCase(el.getAttribute("text-anchor"))) {
|
||||
t.setTextOrigin(javafx.geometry.VPos.CENTER);
|
||||
@@ -364,38 +533,40 @@ public final class AssetManager {
|
||||
}
|
||||
case "g" -> {
|
||||
Group g = new Group();
|
||||
renderChildren(el, g);
|
||||
renderChildren(el, g, defsMap);
|
||||
if (el.hasAttribute("stroke")) {
|
||||
Color strokeCol = parseColor(el.getAttribute("stroke"), null);
|
||||
Paint strokePaint = parsePaint(el.getAttribute("stroke"), defsMap, null);
|
||||
double sw = parseDouble(el.getAttribute("stroke-width"), 1.0);
|
||||
for (Node child : g.getChildren()) {
|
||||
if (child instanceof Shape s) {
|
||||
if (strokeCol != null) s.setStroke(strokeCol);
|
||||
if (strokePaint != null) s.setStroke(strokePaint);
|
||||
s.setStrokeWidth(sw);
|
||||
}
|
||||
}
|
||||
}
|
||||
applyElementTransform(el, g);
|
||||
return g;
|
||||
}
|
||||
}
|
||||
|
||||
if (shape != null) {
|
||||
applyShapeStyles(el, shape);
|
||||
applyShapeStyles(el, shape, defsMap);
|
||||
applyElementTransform(el, shape);
|
||||
return shape;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void applyShapeStyles(Element el, Shape shape) {
|
||||
private static void applyShapeStyles(Element el, Shape shape, Map<String, Paint> defsMap) {
|
||||
if (el.hasAttribute("fill")) {
|
||||
shape.setFill(parseColor(el.getAttribute("fill"), Color.TRANSPARENT));
|
||||
shape.setFill(parsePaint(el.getAttribute("fill"), defsMap, Color.TRANSPARENT));
|
||||
} else if (shape instanceof Line) {
|
||||
shape.setFill(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
if (el.hasAttribute("stroke")) {
|
||||
shape.setStroke(parseColor(el.getAttribute("stroke"), Color.TRANSPARENT));
|
||||
shape.setStroke(parsePaint(el.getAttribute("stroke"), defsMap, Color.TRANSPARENT));
|
||||
}
|
||||
|
||||
if (el.hasAttribute("stroke-width")) {
|
||||
@@ -409,6 +580,22 @@ public final class AssetManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static Paint parsePaint(String str, Map<String, Paint> defsMap, Paint fallback) {
|
||||
if (str == null || str.isBlank() || "none".equalsIgnoreCase(str)) {
|
||||
return Color.TRANSPARENT;
|
||||
}
|
||||
String trimmed = str.trim();
|
||||
if (trimmed.startsWith("url(#") && trimmed.endsWith(")")) {
|
||||
String id = trimmed.substring(5, trimmed.length() - 1).trim();
|
||||
Paint defPaint = defsMap.get(id);
|
||||
if (defPaint != null) {
|
||||
return defPaint;
|
||||
}
|
||||
return Color.web("#0288d1");
|
||||
}
|
||||
return parseColor(trimmed, fallback instanceof Color ? (Color) fallback : Color.BLACK);
|
||||
}
|
||||
|
||||
private static void parsePoints(String ptsStr, java.util.List<Double> pointsList) {
|
||||
String[] tokens = ptsStr.trim().split("[,\\s]+");
|
||||
for (String t : tokens) {
|
||||
@@ -446,4 +633,52 @@ public final class AssetManager {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyElementTransform(Element el, Node node) {
|
||||
if (!el.hasAttribute("transform")) return;
|
||||
String tr = el.getAttribute("transform").trim();
|
||||
if (tr.isBlank()) return;
|
||||
|
||||
try {
|
||||
if (tr.contains("rotate(")) {
|
||||
int start = tr.indexOf("rotate(") + 7;
|
||||
int close = tr.indexOf(')', start);
|
||||
if (close > 0) {
|
||||
String[] args = tr.substring(start, close).trim().split("[,\\s]+");
|
||||
if (args.length >= 3) {
|
||||
double deg = parseDouble(args[0], 0);
|
||||
double px = parseDouble(args[1], 0);
|
||||
double py = parseDouble(args[2], 0);
|
||||
node.getTransforms().add(new javafx.scene.transform.Rotate(deg, px, py));
|
||||
} else if (args.length >= 1) {
|
||||
double deg = parseDouble(args[0], 0);
|
||||
node.getTransforms().add(new javafx.scene.transform.Rotate(deg));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tr.contains("translate(")) {
|
||||
int start = tr.indexOf("translate(") + 10;
|
||||
int close = tr.indexOf(')', start);
|
||||
if (close > 0) {
|
||||
String[] args = tr.substring(start, close).trim().split("[,\\s]+");
|
||||
double tx = parseDouble(args[0], 0);
|
||||
double ty = args.length > 1 ? parseDouble(args[1], 0) : 0;
|
||||
node.getTransforms().add(new javafx.scene.transform.Translate(tx, ty));
|
||||
}
|
||||
}
|
||||
if (tr.contains("scale(")) {
|
||||
int start = tr.indexOf("scale(") + 6;
|
||||
int close = tr.indexOf(')', start);
|
||||
if (close > 0) {
|
||||
String[] args = tr.substring(start, close).trim().split("[,\\s]+");
|
||||
double sx = parseDouble(args[0], 1.0);
|
||||
double sy = args.length > 1 ? parseDouble(args[1], sx) : sx;
|
||||
node.getTransforms().add(new javafx.scene.transform.Scale(sx, sy));
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -709,6 +709,10 @@ public final class SvgIcons {
|
||||
}
|
||||
|
||||
public static Node closeIcon(double size) {
|
||||
return AssetManager.getIconOrDefault("close", size, () -> buildCloseIcon(size));
|
||||
}
|
||||
|
||||
public static Node buildCloseIcon(double size) {
|
||||
StackPane box = new StackPane();
|
||||
box.setPrefSize(size, size);
|
||||
box.setMaxSize(size, size);
|
||||
@@ -738,6 +742,10 @@ public final class SvgIcons {
|
||||
}
|
||||
|
||||
public static Node arrowRightIcon(double size, String colorHex) {
|
||||
return AssetManager.getIconOrDefault("arrow-right", size, () -> buildArrowRightIcon(size, colorHex));
|
||||
}
|
||||
|
||||
public static Node buildArrowRightIcon(double size, String colorHex) {
|
||||
StackPane box = new StackPane();
|
||||
box.setPrefSize(size, size);
|
||||
box.setMaxSize(size, size);
|
||||
@@ -764,6 +772,10 @@ public final class SvgIcons {
|
||||
* Ícones vetoriais de Zoom In (+), Zoom Out (-) e Ajustar à Tela.
|
||||
*/
|
||||
public static Node zoomInIcon(double size) {
|
||||
return AssetManager.getIconOrDefault("zoom-in", size, () -> buildZoomInIcon(size));
|
||||
}
|
||||
|
||||
public static Node buildZoomInIcon(double size) {
|
||||
StackPane box = new StackPane();
|
||||
box.setPrefSize(size, size);
|
||||
box.setMaxSize(size, size);
|
||||
@@ -786,6 +798,10 @@ public final class SvgIcons {
|
||||
}
|
||||
|
||||
public static Node zoomOutIcon(double size) {
|
||||
return AssetManager.getIconOrDefault("zoom-out", size, () -> buildZoomOutIcon(size));
|
||||
}
|
||||
|
||||
public static Node buildZoomOutIcon(double size) {
|
||||
StackPane box = new StackPane();
|
||||
box.setPrefSize(size, size);
|
||||
box.setMaxSize(size, size);
|
||||
@@ -808,6 +824,10 @@ public final class SvgIcons {
|
||||
}
|
||||
|
||||
public static Node zoomFitIcon(double size) {
|
||||
return AssetManager.getIconOrDefault("zoom-fit", size, () -> buildZoomFitIcon(size));
|
||||
}
|
||||
|
||||
public static Node buildZoomFitIcon(double size) {
|
||||
StackPane box = new StackPane();
|
||||
box.setPrefSize(size, size);
|
||||
box.setMaxSize(size, size);
|
||||
@@ -828,4 +848,33 @@ public final class SvgIcons {
|
||||
box.getChildren().add(path);
|
||||
return box;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ícone de Atualização / Sincronização.
|
||||
*/
|
||||
public static Node refreshIcon(double size) {
|
||||
return AssetManager.getIconOrDefault("refresh", size, () -> buildRefreshIcon(size));
|
||||
}
|
||||
|
||||
public static Node buildRefreshIcon(double size) {
|
||||
StackPane box = new StackPane();
|
||||
box.setPrefSize(size, size);
|
||||
box.setMaxSize(size, size);
|
||||
box.setMinSize(size, size);
|
||||
box.setAlignment(Pos.CENTER);
|
||||
|
||||
SVGPath path = new SVGPath();
|
||||
path.setContent("M 20 4 L 20 9 L 15 9 M 4 20 L 4 15 L 9 15 M 5.8 9 A 7 7 0 0 1 18.5 7.5 L 20 9 M 4 15 L 5.5 16.5 A 7 7 0 0 0 18.2 15");
|
||||
path.setStroke(Color.web("#0284c7"));
|
||||
path.setStrokeWidth(1.8);
|
||||
path.setStrokeLineCap(javafx.scene.shape.StrokeLineCap.ROUND);
|
||||
path.setFill(Color.TRANSPARENT);
|
||||
|
||||
double scale = size / 24.0;
|
||||
path.setScaleX(scale);
|
||||
path.setScaleY(scale);
|
||||
|
||||
box.getChildren().add(path);
|
||||
return box;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package br.com.cybercore.signer.ui.view;
|
||||
|
||||
import br.com.cybercore.signer.ui.CyberCoreUiConstants;
|
||||
import br.com.cybercore.signer.ui.assets.AssetManager;
|
||||
import br.com.cybercore.signer.ui.components.SvgIcons;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
/**
|
||||
* Painel informativo Sobre o CyberCore Signer.
|
||||
* Painel informativo Sobre o CyberCore Signer com exibição de identidade visual SVG e dados do agente.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public final class AboutView extends VBox {
|
||||
@@ -16,13 +19,12 @@ public final class AboutView extends VBox {
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
setSpacing(16);
|
||||
setSpacing(14);
|
||||
setAlignment(Pos.CENTER);
|
||||
getStyleClass().add("content-pane");
|
||||
|
||||
Label brandLabel = new Label(CyberCoreUiConstants.APP_NAME);
|
||||
brandLabel.getStyleClass().add("brand-title");
|
||||
brandLabel.setStyle("-fx-font-size: 24px;");
|
||||
// Logotipo oficial vetorial SVG (app-logo.svg) com fallback para cristal 3D
|
||||
Node logoNode = AssetManager.getLogoOrDefault(320, 80, () -> SvgIcons.createAppCrystalLogo(54));
|
||||
|
||||
Label versionLabel = new Label("Versão " + CyberCoreUiConstants.APP_VERSION + " • CCSP/1");
|
||||
versionLabel.getStyleClass().add("status-badge-active");
|
||||
@@ -44,9 +46,9 @@ public final class AboutView extends VBox {
|
||||
detailsBox.getChildren().add(lbl);
|
||||
}
|
||||
|
||||
Label copyLabel = new Label("© 2026 CyberCore. Todos os direitos reservados.");
|
||||
Label copyLabel = new Label("© 2026 CyberCore Systems. Todos os direitos reservados.");
|
||||
copyLabel.getStyleClass().add("dialog-subtitle");
|
||||
|
||||
getChildren().addAll(brandLabel, versionLabel, descLabel, detailsBox, copyLabel);
|
||||
getChildren().addAll(logoNode, versionLabel, descLabel, detailsBox, copyLabel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,10 @@ public class ApplicationApprovalDialog {
|
||||
root.setPadding(new Insets(24));
|
||||
root.getStyleClass().add("dialog-container");
|
||||
|
||||
// Cabeçalho
|
||||
// Cabeçalho com Ícone SVG de Escudo
|
||||
HBox header = new HBox(14);
|
||||
header.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
VBox headerBox = new VBox(4);
|
||||
Label titleLabel = new Label("NOVA APLICAÇÃO WEB SOLICITA ACESSO");
|
||||
titleLabel.getStyleClass().add("dialog-header");
|
||||
@@ -48,6 +51,8 @@ public class ApplicationApprovalDialog {
|
||||
subtitleLabel.getStyleClass().add("dialog-subtitle");
|
||||
headerBox.getChildren().addAll(titleLabel, subtitleLabel);
|
||||
|
||||
header.getChildren().addAll(br.com.cybercore.signer.ui.components.SvgIcons.cyberShieldLogo(34), headerBox);
|
||||
|
||||
// Painel de Detalhes da Aplicação
|
||||
VBox detailsBox = new VBox(10);
|
||||
detailsBox.getStyleClass().add("dialog-box");
|
||||
@@ -106,7 +111,7 @@ public class ApplicationApprovalDialog {
|
||||
|
||||
actionsRow.getChildren().addAll(btnReject, btnAuthorize);
|
||||
|
||||
root.getChildren().addAll(headerBox, detailsBox, secWarning, actionsRow);
|
||||
root.getChildren().addAll(header, detailsBox, secWarning, actionsRow);
|
||||
|
||||
Scene scene = new Scene(root);
|
||||
ThemeManager.applyTheme(scene);
|
||||
|
||||
@@ -77,7 +77,7 @@ public final class CertificatesManagerDialog {
|
||||
btnAddA1.getStyleClass().add("btn-primary");
|
||||
btnAddA1.setOnAction(e -> handleImportA1());
|
||||
|
||||
Button btnRefresh = new Button("Atualizar Lista");
|
||||
Button btnRefresh = new Button(" Atualizar Lista", SvgIcons.refreshIcon(14));
|
||||
btnRefresh.getStyleClass().add("btn-secondary");
|
||||
btnRefresh.setOnAction(e -> refreshList());
|
||||
|
||||
@@ -93,7 +93,7 @@ public final class CertificatesManagerDialog {
|
||||
|
||||
HBox footer = new HBox();
|
||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||
Button btnClose = new Button("Fechar");
|
||||
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
||||
btnClose.getStyleClass().add("btn-secondary");
|
||||
btnClose.setOnAction(e -> stage.close());
|
||||
footer.getChildren().add(btnClose);
|
||||
|
||||
@@ -13,6 +13,7 @@ import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@@ -204,14 +205,20 @@ public final class MainAppView {
|
||||
|
||||
private void updateThemeToggleButton() {
|
||||
boolean dark = ThemeManager.isDark();
|
||||
HBox content = new HBox(6);
|
||||
HBox content = new HBox(8);
|
||||
content.setAlignment(Pos.CENTER);
|
||||
|
||||
Node icon = dark ? SvgIcons.sunIcon(15) : SvgIcons.moonIcon(15);
|
||||
StackPane iconBox = new StackPane(icon);
|
||||
iconBox.setPrefSize(18, 18);
|
||||
iconBox.setMinSize(18, 18);
|
||||
iconBox.setMaxSize(18, 18);
|
||||
iconBox.setAlignment(Pos.CENTER);
|
||||
|
||||
Label label = new Label(dark ? "Modo Claro" : "Modo Escuro");
|
||||
label.setStyle("-fx-font-size: 11.5px; -fx-font-weight: bold;");
|
||||
|
||||
content.getChildren().addAll(icon, label);
|
||||
content.getChildren().addAll(iconBox, label);
|
||||
themeToggleBtn.setGraphic(content);
|
||||
}
|
||||
|
||||
@@ -223,16 +230,22 @@ public final class MainAppView {
|
||||
btn.setPrefHeight(68);
|
||||
btn.setMinHeight(68);
|
||||
|
||||
HBox content = new HBox(10);
|
||||
HBox content = new HBox(12);
|
||||
content.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
Label lbl = new Label(text);
|
||||
lbl.getStyleClass().addAll("cyber-action-text", "serpro-action-text");
|
||||
lbl.setWrapText(true);
|
||||
lbl.setAlignment(Pos.CENTER_LEFT);
|
||||
HBox.setHgrow(lbl, Priority.ALWAYS);
|
||||
|
||||
if (icon != null) {
|
||||
content.getChildren().addAll(icon, lbl);
|
||||
StackPane iconSlot = new StackPane(icon);
|
||||
iconSlot.setPrefSize(40, 40);
|
||||
iconSlot.setMinSize(40, 40);
|
||||
iconSlot.setMaxSize(40, 40);
|
||||
iconSlot.setAlignment(Pos.CENTER);
|
||||
content.getChildren().addAll(iconSlot, lbl);
|
||||
} else {
|
||||
content.getChildren().add(lbl);
|
||||
}
|
||||
@@ -251,16 +264,22 @@ public final class MainAppView {
|
||||
btn.setMinHeight(42);
|
||||
btn.setMaxWidth(Double.MAX_VALUE);
|
||||
|
||||
HBox content = new HBox(10);
|
||||
HBox content = new HBox(12);
|
||||
content.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
Label lbl = new Label(text);
|
||||
lbl.getStyleClass().addAll("cyber-side-text", "serpro-side-text");
|
||||
lbl.setWrapText(true);
|
||||
lbl.setWrapText(false);
|
||||
lbl.setAlignment(Pos.CENTER_LEFT);
|
||||
HBox.setHgrow(lbl, Priority.ALWAYS);
|
||||
|
||||
if (icon != null) {
|
||||
content.getChildren().addAll(icon, lbl);
|
||||
StackPane iconSlot = new StackPane(icon);
|
||||
iconSlot.setPrefSize(26, 26);
|
||||
iconSlot.setMinSize(26, 26);
|
||||
iconSlot.setMaxSize(26, 26);
|
||||
iconSlot.setAlignment(Pos.CENTER);
|
||||
content.getChildren().addAll(iconSlot, lbl);
|
||||
} else {
|
||||
content.getChildren().add(lbl);
|
||||
}
|
||||
|
||||
@@ -488,7 +488,7 @@ public final class PdfSignDialog {
|
||||
resizeToolBox.setAlignment(Pos.CENTER_LEFT);
|
||||
resizeToolBox.setStyle("-fx-background-color: -fx-bg-primary; -fx-border-color: -fx-border-light; -fx-border-radius: 6px; -fx-padding: 2px 6px;");
|
||||
|
||||
btnToggleResize = new Button("📏 Redimensionar");
|
||||
btnToggleResize = new Button(" Redimensionar", SvgIcons.zoomFitIcon(13));
|
||||
btnToggleResize.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||
btnToggleResize.setTooltip(new Tooltip("Ativar/desativar alça interativa para alterar o tamanho do selo"));
|
||||
btnToggleResize.setOnAction(e -> toggleResizeMode());
|
||||
@@ -496,7 +496,7 @@ public final class PdfSignDialog {
|
||||
lblSealScaleIndicator = new Label("100%");
|
||||
lblSealScaleIndicator.setStyle("-fx-font-size: 11px; -fx-font-weight: bold; -fx-min-width: 36px; -fx-alignment: center;");
|
||||
|
||||
btnResetSealScale = new Button("↺ 100%");
|
||||
btnResetSealScale = new Button(" 100%", SvgIcons.refreshIcon(12));
|
||||
btnResetSealScale.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||
btnResetSealScale.setTooltip(new Tooltip("Restaurar tamanho padrão original (100%)"));
|
||||
btnResetSealScale.setVisible(false);
|
||||
@@ -507,7 +507,7 @@ public final class PdfSignDialog {
|
||||
Region tbSpacer = new Region();
|
||||
HBox.setHgrow(tbSpacer, Priority.ALWAYS);
|
||||
|
||||
lblPositionFeedback = new Label("👉 Clique na folha para posicionar");
|
||||
lblPositionFeedback = new Label("Clique na folha para posicionar");
|
||||
lblPositionFeedback.setStyle("-fx-font-size: 11.5px; -fx-text-fill: -fx-accent; -fx-font-weight: bold;");
|
||||
|
||||
toolbar.getChildren().addAll(pageNavBox, zoomBox, resizeToolBox, tbSpacer, lblPositionFeedback);
|
||||
|
||||
@@ -5,6 +5,7 @@ import br.com.cybercore.signer.core.domain.ManagedCertificate;
|
||||
import br.com.cybercore.signer.core.domain.SigningContext;
|
||||
import br.com.cybercore.signer.core.security.PinSessionManager;
|
||||
import br.com.cybercore.signer.ui.CyberCoreUiConstants;
|
||||
import br.com.cybercore.signer.ui.components.SvgIcons;
|
||||
import br.com.cybercore.signer.ui.theme.ThemeManager;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
@@ -60,13 +61,23 @@ public class SignaturePromptDialog {
|
||||
root.getStyleClass().add("dialog-container");
|
||||
root.setPrefWidth(540);
|
||||
|
||||
Label header = new Label(isAuthentication ? "🔑 SOLICITAÇÃO DE LOGIN DIGITAL" : "SOLICITAÇÃO DE ASSINATURA");
|
||||
HBox headerBox = new HBox(12);
|
||||
headerBox.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
VBox titleBox = new VBox(2);
|
||||
Label header = new Label(isAuthentication ? "SOLICITAÇÃO DE LOGIN DIGITAL" : "SOLICITAÇÃO DE ASSINATURA");
|
||||
header.getStyleClass().add("dialog-header");
|
||||
|
||||
Label desc = new Label(isAuthentication
|
||||
? "Uma aplicação web está solicitando autenticação de identidade com seu Certificado Digital ICP-Brasil."
|
||||
: "Uma aplicação web está solicitando a assinatura digital de um documento.");
|
||||
desc.getStyleClass().add("brand-subtitle");
|
||||
titleBox.getChildren().addAll(header, desc);
|
||||
|
||||
headerBox.getChildren().addAll(
|
||||
isAuthentication ? SvgIcons.certificatesIcon() : SvgIcons.cyberShieldLogo(26),
|
||||
titleBox
|
||||
);
|
||||
|
||||
// Caixa de Detalhes
|
||||
VBox detailsBox = new VBox(12);
|
||||
@@ -231,7 +242,7 @@ public class SignaturePromptDialog {
|
||||
|
||||
actionsRow.getChildren().addAll(btnCancel, btnConfirm);
|
||||
|
||||
root.getChildren().addAll(header, desc, detailsBox, validationLabel, actionsRow);
|
||||
root.getChildren().addAll(headerBox, detailsBox, validationLabel, actionsRow);
|
||||
|
||||
Scene scene = new Scene(root);
|
||||
ThemeManager.applyTheme(scene);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Pasta de Ícones (SVG) e Imagens (PNG) — CyberCore Signer
|
||||
# Recursos Visuais (Ícones e Imagens em SVG) — CyberCore Signer
|
||||
|
||||
Esta pasta é o repositório oficial onde você pode visualizar, customizar e substituir livremente os **ícones em SVG** e as **imagens em PNG** do **CyberCore Signer**.
|
||||
Esta pasta é o repositório oficial onde você pode visualizar, customizar e substituir livremente todos os **ícones em SVG** e as **imagens em SVG** do **CyberCore Signer**.
|
||||
|
||||
---
|
||||
|
||||
@@ -31,13 +31,23 @@ assets/
|
||||
│ ├── seal.svg # Ícone Designer e Gerenciador de Selos Notariais
|
||||
│ ├── check.svg # Ícone Checkmark de sucesso e validação
|
||||
│ ├── error.svg # Ícone de Erro / Alerta em vermelho
|
||||
│ └── star.svg # Ícone de Estrela notarial / favorita
|
||||
│ ├── star.svg # Ícone de Estrela notarial / favorita
|
||||
│ ├── close.svg # Ícone Fechar / Cancelar (cruz geométrica moderna)
|
||||
│ ├── arrow-right.svg # Ícone Avançar / Seta direcional
|
||||
│ ├── zoom-in.svg # Ícone Zoom In (+)
|
||||
│ ├── zoom-out.svg # Ícone Zoom Out (-)
|
||||
│ ├── zoom-fit.svg # Ícone Ajustar à Tela
|
||||
│ └── refresh.svg # Ícone Atualizar / Recarregar listas e status
|
||||
│
|
||||
└── images/ # Imagens de alta fidelidade em PNG com canal alfa (transparência)
|
||||
├── app-icon.png # Ícone oficial do app (256x256 px - Diamante 3D azul Serpro)
|
||||
├── app-logo.png # Logotipo horizontal oficial (480x120 px - CyberCore SIGNER)
|
||||
├── brand-banner.png # Banner de cabeçalho e marca (600x160 px - Serpro Blue)
|
||||
└── default-seal.png # Selo de assinatura padrão ICP-Brasil (640x164 px)
|
||||
└── images/ # Imagens vetoriais SVG editáveis (com cópias PNG de fallback)
|
||||
├── app-icon.svg # Ícone oficial do app em SVG (Cristal/Diamante 3D azul)
|
||||
├── app-icon.png # Fallback em PNG (256x256 px)
|
||||
├── app-logo.svg # Logotipo horizontal oficial em SVG (CyberCore SIGNER)
|
||||
├── app-logo.png # Fallback em PNG (480x120 px)
|
||||
├── brand-banner.svg # Banner de cabeçalho e marca em SVG (600x160 px)
|
||||
├── brand-banner.png # Fallback em PNG (600x160 px)
|
||||
├── default-seal.svg # Selo de assinatura padrão ICP-Brasil em SVG (640x164 px)
|
||||
└── default-seal.png # Fallback em PNG (640x164 px)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -45,20 +55,21 @@ assets/
|
||||
## 🎨 Como Customizar Qualquer Ícone ou Imagem
|
||||
|
||||
1. **Ícones:**
|
||||
- Estão no formato `.svg` padrão.
|
||||
- Podem ser editados diretamente no seu editor de código (VS Code, etc.) ou em editores vetoriais como **Inkscape, Figma ou Illustrator**.
|
||||
- Se preferir colocar uma imagem bitmap para um ícone específico, você também pode colocar um `.png` (ex: `pdf.png`). O sistema prioriza `.svg` e aceita `.png` com fallback transparente.
|
||||
- Todos estão no formato `.svg` padrão da W3C.
|
||||
- Podem ser abertos e editados diretamente em editores de texto/código (VS Code) ou ferramentas vetoriais como **Inkscape, Figma, Adobe Illustrator**.
|
||||
- Se preferir usar um arquivo bitmap para algum ícone específico, basta salvar um `.png` correspondente na pasta (ex: `pdf.png`). O sistema detecta e renderiza automaticamente.
|
||||
|
||||
2. **Imagens do Sistema:**
|
||||
- Estão no formato `.png` com canal alfa de 32 bits (fundo transparente).
|
||||
- Você pode substituir `app-logo.png`, `app-icon.png`, `brand-banner.png` ou `default-seal.png` pelo logotipo e identidade visual da sua instituição ou empresa.
|
||||
- Tanto o logotipo horizontal (`app-logo.svg`), o ícone do sistema (`app-icon.svg`), o banner institucional (`brand-banner.svg`) e o selo notarial padrão (`default-seal.svg`) agora são arquivos **SVG vetoriais editáveis**.
|
||||
- Você pode alterar cores, tipografias, brasões e dimensões diretamente nos arquivos SVG.
|
||||
- O `AssetManager` renderiza os arquivos SVG preservando fidelidade geométrica e suportando gradientes e alinhamentos.
|
||||
|
||||
3. **Ordem de Prioridade e Hot-Reloading:**
|
||||
Quando a aplicação busca um ícone ou imagem, a resolução ocorre na ordem:
|
||||
1. **Pasta Física Local (`./assets/icons/` e `./assets/images/`)**: Qualquer alteração feita aqui entra em vigor imediatamente ao abrir a janela ou reiniciar a aplicação, sem necessidade de recompilar!
|
||||
2. **Pasta do Usuário (`~/.cybercore/assets/`)**: Configurações específicas por usuário do sistema operacional.
|
||||
3. **Resources Empacotados no JAR (`/assets/`)**: Cópia empacotada internamente no `.jar`.
|
||||
4. **Fallback Vetorial Nativo (`SvgIcons`)**: Garantia de que a interface nunca fique em branco caso algum arquivo seja excluído.
|
||||
3. **Ordem de Resolução e Carregamento:**
|
||||
Ao requisitar qualquer elemento gráfico, a aplicação segue a ordem de prioridade:
|
||||
1. **Pasta Física Local (`./assets/icons/` e `./assets/images/`)**: Arquivos `.svg` editados aqui têm prioridade máxima e entram em vigor imediatamente sem necessidade de recompilar.
|
||||
2. **Pasta do Usuário (`~/.cybercore/assets/`)**: Para customizações no diretório pessoal do usuário.
|
||||
3. **Recursos Empacotados no JAR (`/assets/`)**: SVGs e imagens internas empacotadas no pacote de distribuição.
|
||||
4. **Fallback Vetorial Nativo (`SvgIcons`)**: Garantia de que a interface nunca fique desprovida de grafismos caso algum arquivo seja removido.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<circle cx="24" cy="24" r="20" fill="#0288d1"/>
|
||||
<circle cx="24" cy="14" r="3" fill="#ffffff"/>
|
||||
<line x1="24" y1="21" x2="24" y2="34" stroke="#ffffff" stroke-width="4.5" stroke-linecap="round"/>
|
||||
<!-- Círculo azul e letra 'i' informativa centrada em (24, 24) -->
|
||||
<circle cx="24" cy="24" r="18" fill="#0288d1"/>
|
||||
<circle cx="24" cy="15" r="2.5" fill="#ffffff"/>
|
||||
<line x1="24" y1="21" x2="24" y2="33" stroke="#ffffff" stroke-width="4.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 291 B After Width: | Height: | Size: 362 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path d="M 8 5 L 15 12 L 8 19" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 221 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path d="M 6 6 L 18 18 M 18 6 L 6 18" fill="none" stroke="#64748b" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 228 B |
@@ -1,6 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<!-- Lua crescente ciano/índigo -->
|
||||
<path d="M 40 26.5 A 18 18 0 1 1 21.5 8 A 14 14 0 0 0 40 26.5 Z" fill="#38bdf8"/>
|
||||
<circle cx="36" cy="12" r="1.5" fill="#818cf8"/>
|
||||
<circle cx="42" cy="18" r="1" fill="#818cf8"/>
|
||||
<!-- Lua crescente ciano/índigo perfeitamente centrada em (24, 24) -->
|
||||
<path d="M 24 8 A 16 16 0 1 0 40 24 A 14 14 0 0 1 24 8 Z" fill="#38bdf8"/>
|
||||
<circle cx="34" cy="14" r="1.8" fill="#818cf8"/>
|
||||
<circle cx="38" cy="22" r="1.2" fill="#818cf8"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 314 B After Width: | Height: | Size: 344 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path d="M 20 4 L 20 9 L 15 9" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M 4 20 L 4 15 L 9 15" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M 5.8 9 A 7 7 0 0 1 18.5 7.5 L 20 9 M 4 15 L 5.5 16.5 A 7 7 0 0 0 18.2 15" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 510 B |
@@ -1,8 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<!-- Engrenagem simétrica de 8 dentes centrada em (24, 24) -->
|
||||
<path d="M 21 4 L 27 4 L 28 10 C 30 10.8 31.8 11.8 33.4 13.2 L 39 10 L 43 14 L 39.8 19.6 C 41.2 21.2 42.2 23 43 25 L 49 26 L 49 32 L 43 33 C 42.2 35 41.2 36.8 39.8 38.4 L 43 44 L 39 48 L 33.4 44.8 C 31.8 46.2 30 47.2 28 48 L 27 54 L 21 54 L 20 48 C 18 47.2 16.2 46.2 14.6 44.8 L 9 48 L 5 44 L 8.2 38.4 C 6.8 36.8 5.8 35 5 33 L -1 32 L -1 26 L 5 25 C 5.8 23 6.8 21.2 8.2 19.6 L 5 14 L 9 10 L 14.6 13.2 C 16.2 11.8 18 10.8 20 10 Z" transform="scale(0.8) translate(6, 4)" fill="#0288d1"/>
|
||||
<!-- Círculo externo da engrenagem -->
|
||||
<circle cx="24" cy="24" r="14" fill="#0288d1"/>
|
||||
<!-- Furo central vazado -->
|
||||
<circle cx="24" cy="24" r="6" fill="#ffffff"/>
|
||||
<!-- Engrenagem corporativa simétrica centrada em (24, 24) -->
|
||||
<path fill="#0288d1" fill-rule="evenodd" d="M 21.5 5 L 26.5 5 L 27.2 10.2 C 29 10.9 30.7 11.9 32.2 13.1 L 36.6 10.6 L 40.1 14.1 L 37.6 18.5 C 38.8 20 39.8 21.7 40.5 23.5 L 45.7 24.2 L 45.7 29.2 L 40.5 29.9 C 39.8 31.7 38.8 33.4 37.6 34.9 L 40.1 39.3 L 36.6 42.8 L 32.2 40.3 C 30.7 41.5 29 42.5 27.2 43.2 L 26.5 48.4 L 21.5 48.4 L 20.8 43.2 C 19 42.5 17.3 41.5 15.8 40.3 L 11.4 42.8 L 7.9 39.3 L 10.4 34.9 C 9.2 33.4 8.2 31.7 7.5 29.9 L 2.3 29.2 L 2.3 24.2 L 7.5 23.5 C 8.2 21.7 9.2 20 10.4 18.5 L 7.9 14.1 L 11.4 10.6 L 15.8 13.1 C 17.3 11.9 19 10.9 20.8 10.2 Z M 24 17.5 A 6.5 6.5 0 1 0 24 30.5 A 6.5 6.5 0 1 0 24 17.5 Z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 817 B After Width: | Height: | Size: 784 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path d="M 4 9 L 4 4 L 9 4 M 15 4 L 20 4 L 20 9 M 20 15 L 20 20 L 15 20 M 9 20 L 4 20 L 4 15" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 284 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<circle cx="10" cy="10" r="7" fill="none" stroke="#0284c7" stroke-width="2.2"/>
|
||||
<path d="M 15 15 L 21 21" fill="none" stroke="#0284c7" stroke-width="2.5" stroke-linecap="round"/>
|
||||
<path d="M 10 7 L 10 13 M 7 10 L 13 10" fill="none" stroke="#0284c7" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 387 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<circle cx="10" cy="10" r="7" fill="none" stroke="#0284c7" stroke-width="2.2"/>
|
||||
<path d="M 15 15 L 21 21" fill="none" stroke="#0284c7" stroke-width="2.5" stroke-linecap="round"/>
|
||||
<path d="M 7 10 L 13 10" fill="none" stroke="#0284c7" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 372 B |
@@ -0,0 +1,37 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256">
|
||||
<defs>
|
||||
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#1976d2"/>
|
||||
<stop offset="45%" stop-color="#0d47a1"/>
|
||||
<stop offset="100%" stop-color="#01579b"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="topFacetGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.9"/>
|
||||
<stop offset="100%" stop-color="#80d8ff" stop-opacity="0.55"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="leftFacetGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#40c4ff" stop-opacity="0.75"/>
|
||||
<stop offset="100%" stop-color="#0288d1" stop-opacity="0.35"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="rightFacetGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#e1f5fe" stop-opacity="0.85"/>
|
||||
<stop offset="100%" stop-color="#29b6f6" stop-opacity="0.45"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="centerGlow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.95"/>
|
||||
<stop offset="100%" stop-color="#ffffff" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Fundo com cantos arredondados -->
|
||||
<rect x="8" y="8" width="240" height="240" rx="68" ry="68" fill="url(#bgGrad)"/>
|
||||
<rect x="10" y="10" width="236" height="236" rx="66" ry="66" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-opacity="0.25"/>
|
||||
|
||||
<!-- Facetas 3D do Cristal / Diamante Azul -->
|
||||
<polygon points="128,46 210,113 128,143 46,113" fill="url(#topFacetGrad)"/>
|
||||
<polygon points="46,113 128,143 128,215 72,179" fill="url(#leftFacetGrad)"/>
|
||||
<polygon points="210,113 128,143 128,215 184,179" fill="url(#rightFacetGrad)"/>
|
||||
|
||||
<!-- Brilho estelar central -->
|
||||
<circle cx="128" cy="128" r="38" fill="url(#centerGlow)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,41 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 120" width="480" height="120">
|
||||
<defs>
|
||||
<linearGradient id="logoIconBg" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#1976d2"/>
|
||||
<stop offset="50%" stop-color="#0d47a1"/>
|
||||
<stop offset="100%" stop-color="#01579b"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="logoTopFacet" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.95"/>
|
||||
<stop offset="100%" stop-color="#80d8ff" stop-opacity="0.6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="logoLeftFacet" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#40c4ff" stop-opacity="0.8"/>
|
||||
<stop offset="100%" stop-color="#0288d1" stop-opacity="0.4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="logoRightFacet" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#e1f5fe" stop-opacity="0.9"/>
|
||||
<stop offset="100%" stop-color="#29b6f6" stop-opacity="0.5"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Container do Ícone Cristal 3D -->
|
||||
<rect x="14" y="16" width="88" height="88" rx="24" ry="24" fill="url(#logoIconBg)"/>
|
||||
<rect x="16" y="18" width="84" height="84" rx="22" ry="22" fill="none" stroke="#ffffff" stroke-width="1.5" stroke-opacity="0.25"/>
|
||||
|
||||
<!-- Facetas Cristalinas (cx=58, cy=60, sz=63) -->
|
||||
<polygon points="58,32 86,55 58,66 30,55" fill="url(#logoTopFacet)"/>
|
||||
<polygon points="30,55 58,66 58,92 39,79" fill="url(#logoLeftFacet)"/>
|
||||
<polygon points="86,55 58,66 58,92 77,79" fill="url(#logoRightFacet)"/>
|
||||
<circle cx="58" cy="60" r="14" fill="#ffffff" fill-opacity="0.3"/>
|
||||
|
||||
<!-- Tipografia Oficial "CyberCore" -->
|
||||
<text x="124" y="62" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="42" font-weight="bold" fill="#0a2540">Cyber<tspan fill="#0288d1">Core</tspan></text>
|
||||
|
||||
<!-- Badge "SIGNER" -->
|
||||
<rect x="124" y="76" width="94" height="22" rx="6" ry="6" fill="#0288d1"/>
|
||||
<text x="139" y="92" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="13" font-weight="bold" fill="#ffffff" letter-spacing="1">SIGNER</text>
|
||||
|
||||
<!-- Subtítulo ICP-Brasil -->
|
||||
<text x="230" y="92" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#64748b">ICP-Brasil • Assinador Digital Seguro</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,32 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 160" width="600" height="160">
|
||||
<defs>
|
||||
<linearGradient id="bannerBgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#0d47a1"/>
|
||||
<stop offset="60%" stop-color="#0284c7"/>
|
||||
<stop offset="100%" stop-color="#0369a1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="bannerIconGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#1e88e5"/>
|
||||
<stop offset="100%" stop-color="#01579b"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Fundo do Banner -->
|
||||
<rect x="0" y="0" width="600" height="160" rx="16" ry="16" fill="url(#bannerBgGrad)"/>
|
||||
|
||||
<!-- Formas geométricas de segurança no fundo -->
|
||||
<circle cx="480" cy="50" r="110" fill="#ffffff" fill-opacity="0.08"/>
|
||||
<circle cx="540" cy="120" r="80" fill="#ffffff" fill-opacity="0.06"/>
|
||||
<circle cx="380" cy="-20" r="90" fill="#ffffff" fill-opacity="0.04"/>
|
||||
|
||||
<!-- Ícone Cristal à esquerda -->
|
||||
<rect x="30" y="35" width="90" height="90" rx="22" ry="22" fill="url(#bannerIconGrad)"/>
|
||||
<rect x="33" y="38" width="84" height="84" rx="19" ry="19" fill="none" stroke="#ffffff" stroke-width="2" stroke-opacity="0.3"/>
|
||||
<polygon points="75,52 101,80 75,108 49,80" fill="#ffffff" fill-opacity="0.9"/>
|
||||
<polygon points="75,52 75,108 49,80" fill="#80d8ff" fill-opacity="0.5"/>
|
||||
|
||||
<!-- Textos do Banner -->
|
||||
<text x="148" y="74" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="34" font-weight="bold" fill="#ffffff">CyberCore Signer</text>
|
||||
<text x="148" y="104" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="16" font-weight="normal" fill="#e0f2fe">Agente de Assinatura Digital ICP-Brasil A1 & A3</text>
|
||||
<text x="148" y="128" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="12" font-weight="bold" fill="#38bdf8" letter-spacing="1">PADES • XADES • CADES • TIMESTAMPS • CCSP/1</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,34 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 164" width="640" height="164">
|
||||
<defs>
|
||||
<linearGradient id="badgeGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#0288d1"/>
|
||||
<stop offset="100%" stop-color="#01579b"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Fundo do Selo -->
|
||||
<rect x="2" y="2" width="636" height="160" rx="12" ry="12" fill="#f8fafc" stroke="#0288d1" stroke-width="2.5"/>
|
||||
|
||||
<!-- Brasão Notarial no lado esquerdo -->
|
||||
<rect x="18" y="16" width="130" height="132" rx="10" ry="10" fill="url(#badgeGrad)"/>
|
||||
|
||||
<!-- Textos do Brasão -->
|
||||
<text x="83" y="44" font-family="'Segoe UI', Roboto, sans-serif" font-size="12" font-weight="bold" fill="#ffffff" text-anchor="middle" letter-spacing="0.5">ASSINADO</text>
|
||||
<text x="83" y="60" font-family="'Segoe UI', Roboto, sans-serif" font-size="11" font-weight="bold" fill="#ffffff" text-anchor="middle" letter-spacing="0.5">DIGITALMENTE</text>
|
||||
|
||||
<!-- Estrela dourada -->
|
||||
<circle cx="83" cy="85" r="14" fill="#fbbf24"/>
|
||||
<text x="83" y="91" font-family="'Segoe UI', sans-serif" font-size="16" font-weight="bold" fill="#ffffff" text-anchor="middle">★</text>
|
||||
|
||||
<text x="83" y="126" font-family="'Segoe UI', Roboto, sans-serif" font-size="10" font-weight="bold" fill="#e0f2fe" text-anchor="middle" letter-spacing="1">ICP-BRASIL</text>
|
||||
|
||||
<!-- Linha divisória vertical -->
|
||||
<line x1="166" y1="16" x2="166" y2="148" stroke="#cfd8dc" stroke-width="1.5"/>
|
||||
|
||||
<!-- Informações da Assinatura -->
|
||||
<text x="184" y="36" font-family="'Segoe UI', Roboto, sans-serif" font-size="11" font-weight="bold" fill="#64748b" letter-spacing="0.5">DOCUMENTO ASSINADO DIGITALMENTE POR:</text>
|
||||
<text x="184" y="60" font-family="'Segoe UI', Roboto, sans-serif" font-size="17" font-weight="bold" fill="#0a2540">NOME DO TITULAR DO CERTIFICADO</text>
|
||||
<text x="184" y="82" font-family="'Segoe UI', Roboto, sans-serif" font-size="12" font-weight="normal" fill="#334155">CPF: ***.000.000-**</text>
|
||||
<text x="184" y="102" font-family="'Segoe UI', Roboto, sans-serif" font-size="12" font-weight="normal" fill="#334155">Data e Hora: 11/10/2026 12:00:00 -03:00</text>
|
||||
<text x="184" y="128" font-family="'Segoe UI', Roboto, sans-serif" font-size="11" font-style="italic" fill="#0288d1">A conformidade com a ICP-Brasil pode ser verificada no ITI.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,58 @@
|
||||
package br.com.cybercore.signer.ui.assets;
|
||||
|
||||
import br.com.cybercore.signer.ui.components.SvgIcons;
|
||||
import javafx.scene.Node;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class AssetManagerTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Deve carregar imagens institucionais em SVG do classpath ou pasta de assets")
|
||||
void shouldLoadSvgImages() {
|
||||
Node logo = AssetManager.getImageOrDefault("app-logo", 480, 120, null);
|
||||
assertThat(logo).isNotNull();
|
||||
|
||||
Node banner = AssetManager.getImageOrDefault("brand-banner", 600, 160, null);
|
||||
assertThat(banner).isNotNull();
|
||||
|
||||
Node icon = AssetManager.getImageOrDefault("app-icon", 256, 256, null);
|
||||
assertThat(icon).isNotNull();
|
||||
|
||||
Node seal = AssetManager.getImageOrDefault("default-seal", 640, 164, null);
|
||||
assertThat(seal).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Deve carregar ícones SVG dinâmicos através do AssetManager")
|
||||
void shouldLoadSvgIcons() {
|
||||
String[] iconNames = {
|
||||
"pdf", "xml", "file", "batch", "attached", "co-sign",
|
||||
"timestamp", "ltv", "validate", "validate-timestamp", "exit",
|
||||
"logs", "settings", "tutorial", "cert", "about", "sun", "moon",
|
||||
"shield", "seal", "check", "error", "star", "close", "arrow-right",
|
||||
"zoom-in", "zoom-out", "zoom-fit", "refresh"
|
||||
};
|
||||
|
||||
for (String name : iconNames) {
|
||||
Node iconNode = AssetManager.getIconOrDefault(name, 24, null);
|
||||
assertThat(iconNode).withFailMessage("Ícone SVG não encontrado ou nulo: %s", name).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Deve instanciar nós de ícones via fábrica SvgIcons com fallback")
|
||||
void shouldCreateNodesViaSvgIcons() {
|
||||
assertThat(SvgIcons.closeIcon(16)).isNotNull();
|
||||
assertThat(SvgIcons.arrowRightIcon(16)).isNotNull();
|
||||
assertThat(SvgIcons.zoomInIcon(16)).isNotNull();
|
||||
assertThat(SvgIcons.zoomOutIcon(16)).isNotNull();
|
||||
assertThat(SvgIcons.zoomFitIcon(16)).isNotNull();
|
||||
assertThat(SvgIcons.refreshIcon(16)).isNotNull();
|
||||
assertThat(SvgIcons.createAppCrystalLogo(48)).isNotNull();
|
||||
}
|
||||
}
|
||||