From 5551d9e66735e80f88d5729cb75286b7579829e5 Mon Sep 17 00:00:00 2001 From: Claudecio Martins Date: Mon, 12 Oct 2026 13:40:07 -0300 Subject: [PATCH] docs: add SVG design guide and update PDF and XML asset files --- assets/README.md | 1 + assets/icons/pdf.svg | 109 +++--------------- assets/icons/xml.svg | 100 ++-------------- docs/svg-design-guide.md | 86 ++++++++++++++ .../signer/ui/assets/AssetManager.java | 46 ++++++-- .../src/main/resources/assets/icons/pdf.svg | 109 +++--------------- .../src/main/resources/assets/icons/xml.svg | 100 ++-------------- .../signer/ui/assets/AssetManagerTest.java | 18 +++ 8 files changed, 185 insertions(+), 384 deletions(-) create mode 100644 docs/svg-design-guide.md 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()); + } + } }