diff --git a/assets/README.md b/assets/README.md
index 91f8eef..ee325f1 100644
--- a/assets/README.md
+++ b/assets/README.md
@@ -56,6 +56,7 @@ assets/
1. **Ícones:**
- Todos estão no formato `.svg` padrão da W3C.
+ - Consulte o guia completo de especificações em [docs/svg-design-guide.md](../docs/svg-design-guide.md) para tokens de cores, dimensões e boas práticas ao exportar do **Inkscape/Figma**.
- 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.
diff --git a/assets/icons/pdf.svg b/assets/icons/pdf.svg
index 95d14b5..c8668ba 100644
--- a/assets/icons/pdf.svg
+++ b/assets/icons/pdf.svg
@@ -1,96 +1,13 @@
-
-PDF
+
+
+
+
+
+
+
+
+
+
+
+ PDF
+
diff --git a/assets/icons/xml.svg b/assets/icons/xml.svg
index 87a9bec..cb15c6b 100644
--- a/assets/icons/xml.svg
+++ b/assets/icons/xml.svg
@@ -1,97 +1,13 @@
-
-
-
-
+
-
+
-
+
-
-
-
+
+
+
-
- <XML>
+
+ <XML>
diff --git a/docs/svg-design-guide.md b/docs/svg-design-guide.md
new file mode 100644
index 0000000..9d2e555
--- /dev/null
+++ b/docs/svg-design-guide.md
@@ -0,0 +1,86 @@
+# Guia de Padrão Visual e Design System SVG — CyberCore Signer
+
+Este documento estabelece a especificação oficial de design, geometria, tokens de cores e regras de compatibilidade para todos os ícones e elementos gráficos vetoriais (**SVG**) do ecossistema **CyberCore Signer**.
+
+---
+
+## 1. Princípios de Design
+
+1. **Clareza e Precisão:** Cada ícone deve comunicar imediatamente sua função técnica (PDF, XML, assinatura embutida, validação, credenciais).
+2. **Minimalismo e Geometria Pura:** Evitar ruídos visuais, nós desnecessários, filtros proprietários ou imagens bitmap embutidas (``).
+3. **Consistência de Traço e Escala:** Espessuras de contorno previsíveis e alinhadas ao grid de 48x48 pixels.
+4. **Interoperabilidade:** Totalmente compatível com editores vetoriais modernos (**Inkscape, Figma, Adobe Illustrator**) e com o parser nativo de JavaFX em `AssetManager`.
+
+---
+
+## 2. Grid e Canvas Canônico
+
+| Propriedade | Valor Padrão | Descrição |
+|---|---|---|
+| **ViewBox** | `0 0 48 48` | Padrão obrigatório para ícones da grade de ações e ferramentas laterais. |
+| **Dimensões Base** | `width="48" height="48"` | Dimensões nominais 1:1. |
+| **Área Segura** | `4px` de margem interna | O conteúdo vetorial deve se concentrar entre `(4, 4)` e `(44, 44)`. |
+| **Raio de Canto Padrão** | `rx="3"` ou `rx="6"` | Arredondamentos harmônicos em cantos de janelas e etiquetas. |
+
+---
+
+## 3. Paleta de Cores e Tokens Visuais
+
+Os ícones utilizam uma paleta de cores corporativa baseada em Material Design e Tailwind Modern, com alto contraste sobre fundos claros e escuros:
+
+| Token | Hexadecimal | Papel / Significado |
+|---|---|---|
+| **`color-brand-primary`** | `#0288d1` | Azul CyberCore. Traço padrão de folhas, botões principais e XML. |
+| **`color-brand-deep`** | `#01579b` | Azul escuro para dobras e acentos de profundidade. |
+| **`color-paper-fold`** | `#b3e5fc` | Preenchimento suave da dobra superior do documento padrão. |
+| **`color-pdf-accent`** | `#d32f2f` | Vermelho institucional do formato Adobe PDF. |
+| **`color-pdf-fold`** | `#ffcdd2` | Preenchimento suave da dobra do documento PDF. |
+| **`color-success`** | `#10b981` / `#2e7d32` | Verde esmeralda para assinaturas válidas e verificações. |
+| **`color-gold-seal`** | `#f59e0b` / `#d97706` | Dourado e âmbar para selos notariais e certificação digital. |
+| **`color-paper-lines`** | `#b0bec5` / `#cfd8dc` | Linhas de texto representativas e divisórias neutras. |
+| **`color-console-bg`** | `#1e293b` | Fundo de janelas de terminal (Logs). |
+| **`color-danger-exit`** | `#d32f2f` | Seta e alertas de encerramento do agente. |
+
+---
+
+## 4. Anatomia dos Ícones de Documentos
+
+Para documentos com dobra (como PDF, XML, Atachada e Validador):
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [TEXTO]
+
+```
+
+---
+
+## 5. Boas Práticas para Exportação no Inkscape / Figma
+
+Ao criar ou editar ícones nas ferramentas vetoriais externas:
+
+1. **Escolha a opção "SVG Simples" (Plain SVG) no Inkscape:**
+ - No Inkscape: `Arquivo → Salvar Como... → SVG Simples (*.svg)`.
+ - Isso elimina metadados de sessão (`sodipodi:namedview`, guias, zoom e histórico).
+2. **Nunca incorpore imagens bitmap (`` base64):**
+ - O aplicativo desktop deve conter exclusivamente geometria vetorial limpa (`path`, `rect`, `circle`, `line`, `polygon`, `polyline`, `text`).
+3. **Suporte a `style="..."` inline:**
+ - O parser do CyberCore Signer (`AssetManager`) suporta tanto atributos XML puros (`fill="#..."`) quanto estilos inline em CSS (`style="fill:#...;stroke:#..."`).
+4. **Mantenha o ViewBox Quadrado:**
+ - Preserve sempre `viewBox="0 0 48 48"` para garantir alinhamento perfeito nos botões e painéis da interface.
diff --git a/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/assets/AssetManager.java b/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/assets/AssetManager.java
index 1fc8938..58fc51a 100644
--- a/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/assets/AssetManager.java
+++ b/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/assets/AssetManager.java
@@ -523,8 +523,11 @@ public final class AssetManager {
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(parsePaint(el.getAttribute("fill"), defsMap, Color.BLACK));
+
+ Map inlineStyle = el.hasAttribute("style") ? parseStyleAttribute(el.getAttribute("style")) : Map.of();
+ String fillVal = inlineStyle.getOrDefault("fill", el.hasAttribute("fill") ? el.getAttribute("fill") : null);
+ if (fillVal != null) {
+ t.setFill(parsePaint(fillVal, defsMap, Color.BLACK));
}
if ("middle".equalsIgnoreCase(el.getAttribute("text-anchor"))) {
t.setTextOrigin(javafx.geometry.VPos.CENTER);
@@ -558,19 +561,46 @@ public final class AssetManager {
return null;
}
+ private static Map parseStyleAttribute(String styleAttr) {
+ Map styleMap = new HashMap<>();
+ if (styleAttr == null || styleAttr.isBlank()) {
+ return styleMap;
+ }
+ String[] declarations = styleAttr.split(";");
+ for (String decl : declarations) {
+ int colonIdx = decl.indexOf(':');
+ if (colonIdx > 0) {
+ String prop = decl.substring(0, colonIdx).trim().toLowerCase();
+ String val = decl.substring(colonIdx + 1).trim();
+ styleMap.put(prop, val);
+ }
+ }
+ return styleMap;
+ }
+
private static void applyShapeStyles(Element el, Shape shape, Map defsMap) {
- if (el.hasAttribute("fill")) {
- shape.setFill(parsePaint(el.getAttribute("fill"), defsMap, Color.TRANSPARENT));
+ Map inlineStyle = el.hasAttribute("style") ? parseStyleAttribute(el.getAttribute("style")) : Map.of();
+
+ String fillVal = inlineStyle.getOrDefault("fill", el.hasAttribute("fill") ? el.getAttribute("fill") : null);
+ if (fillVal != null) {
+ shape.setFill(parsePaint(fillVal, defsMap, Color.TRANSPARENT));
} else if (shape instanceof Line) {
shape.setFill(Color.TRANSPARENT);
}
- if (el.hasAttribute("stroke")) {
- shape.setStroke(parsePaint(el.getAttribute("stroke"), defsMap, Color.TRANSPARENT));
+ String strokeVal = inlineStyle.getOrDefault("stroke", el.hasAttribute("stroke") ? el.getAttribute("stroke") : null);
+ if (strokeVal != null) {
+ shape.setStroke(parsePaint(strokeVal, defsMap, Color.TRANSPARENT));
}
- if (el.hasAttribute("stroke-width")) {
- shape.setStrokeWidth(parseDouble(el.getAttribute("stroke-width"), 1.0));
+ String strokeWidthVal = inlineStyle.getOrDefault("stroke-width", el.hasAttribute("stroke-width") ? el.getAttribute("stroke-width") : null);
+ if (strokeWidthVal != null) {
+ shape.setStrokeWidth(parseDouble(strokeWidthVal, 1.0));
+ }
+
+ String opacityVal = inlineStyle.getOrDefault("opacity", el.hasAttribute("opacity") ? el.getAttribute("opacity") : null);
+ if (opacityVal != null) {
+ shape.setOpacity(parseDouble(opacityVal, 1.0));
}
if (el.hasAttribute("fill-rule") && shape instanceof SVGPath sp) {
diff --git a/modules/cybercore-ui/src/main/resources/assets/icons/pdf.svg b/modules/cybercore-ui/src/main/resources/assets/icons/pdf.svg
index 95d14b5..c8668ba 100644
--- a/modules/cybercore-ui/src/main/resources/assets/icons/pdf.svg
+++ b/modules/cybercore-ui/src/main/resources/assets/icons/pdf.svg
@@ -1,96 +1,13 @@
-
-PDF
+
+
+
+
+
+
+
+
+
+
+
+ PDF
+
diff --git a/modules/cybercore-ui/src/main/resources/assets/icons/xml.svg b/modules/cybercore-ui/src/main/resources/assets/icons/xml.svg
index 87a9bec..cb15c6b 100644
--- a/modules/cybercore-ui/src/main/resources/assets/icons/xml.svg
+++ b/modules/cybercore-ui/src/main/resources/assets/icons/xml.svg
@@ -1,97 +1,13 @@
-
-
-
-
+
-
+
-
+
-
-
-
+
+
+
-
- <XML>
+
+ <XML>
diff --git a/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/assets/AssetManagerTest.java b/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/assets/AssetManagerTest.java
index 0b26b0d..8948b82 100644
--- a/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/assets/AssetManagerTest.java
+++ b/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/assets/AssetManagerTest.java
@@ -55,4 +55,22 @@ class AssetManagerTest {
assertThat(SvgIcons.refreshIcon(16)).isNotNull();
assertThat(SvgIcons.createAppCrystalLogo(48)).isNotNull();
}
+
+ @Test
+ @DisplayName("Deve interpretar atributos style inline no padrão de exportação do Inkscape/Figma")
+ void shouldParseSvgWithInlineStyles() {
+ String svgWithStyle = """
+
+
+ OK
+
+ """;
+ try (var in = new java.io.ByteArrayInputStream(svgWithStyle.getBytes(java.nio.charset.StandardCharsets.UTF_8))) {
+ Node node = AssetManager.parseSvgToNode(in, 48, 48);
+ assertThat(node).isNotNull();
+ assertThat(node).isInstanceOf(javafx.scene.layout.Pane.class);
+ } catch (Exception e) {
+ org.junit.jupiter.api.Assertions.fail("Falha ao analisar SVG com estilo inline: " + e.getMessage());
+ }
+ }
}