SESM Tools
SESM ships three Python command-line tools plus a unittest-based test suite. None of the tools require third-party services; Embed-SESM.py optionally uses the jsonschema library (falling back to manual structural validation if it isn't installed), and Convert-to-SVG.py optionally uses opencv-python for vector tracing (falling back to base64 raster embedding).
Embed-SESM.py
Automatically processes SVG dimensions, strips editor-specific namespace junk (from Inkscape/Sodipodi), scans for visual themes, deep-merges override files, and embeds compliant JSON metadata.
CLI usage:
python Embed-SESM.py [options]
Options:
--input-dir/-i(default:./svg) — directory containing the SVG assets.--overrides/-o(default:./svg-metadata.overrides.json) — path to overrides database.--schema/-s(default:./svg_asset.schema.json) — path to validation JSON Schema.--validate-only— read existing SVG assets and validate their metadata against the schema, without rewriting the files.--verbose/-v— print verbose debug output.
Key features:
- NIPC Theme Detection Heuristics — scans SVGs for NeonInk Palette Contract colors (e.g.
#0B0F1A,#F97316,#22D3EE). If found, it populatestheme.tokens, sets the visual mode, configures semantic state properties, and maps accents. - Deep Merge Overrides — recursively merges metadata overrides from
svg-metadata.overrides.jsonkeyed by asset slug. - Legacy Mapping — harvests legacy metadata formats (mapping
aisummaries/tags tollm.summaryandasset.tags). - Automated Validation — validates all generated/extracted metadata blocks using the
jsonschemalibrary, with a manual structural-validator fallback.
import datetime
import json
import re
import xml.etree.ElementTree as ET
import sys
import argparse
from pathlib import Path
TARGET_SIZE = 300 # logical render size (px)
PROJECT = "aptlantis"
GENERATOR = "aptlantis-svg-asset-worker"
SVG_NS = "http://www.w3.org/2000/svg"
XLINK_NS = "http://www.w3.org/1999/xlink"
ET.register_namespace("", SVG_NS)
ET.register_namespace("xlink", XLINK_NS)
def load_overrides(overrides_path: Path, verbose=False):
if overrides_path.exists():
try:
with open(overrides_path, "r", encoding="utf-8") as f:
data = json.load(f)
# Remove example if it exists
data.pop("_example", None)
if verbose:
print(f"Loaded {len(data)} overrides from {overrides_path.name}")
return data
except Exception as e:
print(f"Error loading overrides from {overrides_path}: {e}")
else:
if verbose:
print(f"Overrides file not found at {overrides_path}; proceeding without overrides.")
return {}
def parse_svg_length(value):
if value is None:
return None
text = str(value).strip()
match = re.match(r"^([0-9]*\.?[0-9]+)", text)
if not match:
return None
try:
return float(match.group(1))
except ValueError:
return None
def get_viewbox(root):
# Keep existing viewBox if it's there
viewbox = root.get("viewBox")
if viewbox:
return viewbox
# Fallback to computing from width and height
width = parse_svg_length(root.get("width"))
height = parse_svg_length(root.get("height"))
if width is not None and height is not None:
return f"0 0 {width} {height}"
return f"0 0 {TARGET_SIZE} {TARGET_SIZE}"
def strip_editor_junk(root):
# Remove any attribute containing inkscape or sodipodi in its key
for key in list(root.attrib.keys()):
if any(junk in key for junk in ["inkscape", "sodipodi"]):
del root.attrib[key]
def deep_merge(dict1, dict2):
"""
Recursively merge dict2 into dict1 in place.
"""
for key, value in dict2.items():
if key in dict1 and isinstance(dict1[key], dict) and isinstance(value, dict):
deep_merge(dict1[key], value)
else:
dict1[key] = value
return dict1
def detect_nipc_theme(svg_text: str):
"""
Scan the SVG XML text for NIPC colors and construct a semantic theme block.
"""
# Find all hex color codes (e.g. #0B0F1A)
hex_colors = set(re.findall(r'#[0-9a-fA-F]{6}\b', svg_text))
# Normalize to lowercase
hex_colors = {c.lower() for c in hex_colors}
nipc_map = {
"#050816": "void",
"#0b0f1a": "base",
"#111827": "panel",
"#22d3ee": "info",
"#a78bfa": "process",
"#f472b6": "featured",
"#34d399": "success",
"#facc15": "important",
"#f43f5e": "critical",
"#f97316": "code_heat",
"#e5e7eb": "text",
"#94a3b8": "muted"
}
detected_tokens = {}
for hc in hex_colors:
if hc in nipc_map:
detected_tokens[nipc_map[hc]] = hc.upper()
if not detected_tokens:
return None
theme_block = {
"id": "neon-ink",
"name": "Neon Ink",
"version": "0.1.0",
"palette_contract": "nipc-0.1",
"mode": "dark",
"tokens": detected_tokens,
"semantic_families": {
"clarity-orientation": ["info", "structure", "navigation", "reference", "orientation"],
"trust-validation": ["success", "verified", "stable", "reproducible", "available"],
"attention-learning-anchor": ["important", "note", "caution", "decision", "memory-anchor"],
"risk-constraint": ["critical", "error", "blocked", "constraint", "deprecated"],
"process-transformation": ["process", "pipeline", "transform", "automation", "orchestration"],
"creation-build-code": ["code-heat", "build", "operation", "artifact-output", "tooling"],
"discovery-creative-featured": ["featured", "creative", "discovery", "spotlight", "human-note"],
"research-experimental": ["experimental", "research", "prototype", "hypothesis", "unstable"],
"canonical-archive-neutral": ["canonical", "archive", "muted", "unknown", "baseline"]
}
}
# Add accent color info if detected
if "code_heat" in detected_tokens:
theme_block["accent"] = {
"name": "code-heat",
"hex": "#F97316",
"semantic_role": "code-heat",
"semantic_family": "creation-build-code",
"psychological_intent": "signal-hands-on-code-build-work"
}
elif "info" in detected_tokens:
theme_block["accent"] = {
"name": "info",
"hex": "#22D3EE",
"semantic_role": "info",
"semantic_family": "clarity-orientation",
"psychological_intent": "understanding-reference-guidance"
}
elif "featured" in detected_tokens:
theme_block["accent"] = {
"name": "featured",
"hex": "#F472B6",
"semantic_role": "featured",
"semantic_family": "discovery-creative-featured",
"psychological_intent": "featured-creative-spotlight"
}
# State tracking based on tokens present
if "critical" in detected_tokens:
theme_block["state"] = {
"name": "error",
"intensity": 3,
"glow": "intense",
"priority": "high"
}
elif "important" in detected_tokens:
theme_block["state"] = {
"name": "warning",
"intensity": 2,
"glow": "soft",
"priority": "high"
}
elif "success" in detected_tokens:
theme_block["state"] = {
"name": "verified",
"intensity": 1,
"glow": "none",
"priority": "medium"
}
else:
theme_block["state"] = {
"name": "active",
"intensity": 2,
"glow": "soft",
"priority": "medium"
}
return theme_block
def validate_sesm_block(sesm_block, schema_path: Path, verbose=False):
# Try using jsonschema
try:
import jsonschema
if schema_path and schema_path.exists():
with open(schema_path, "r", encoding="utf-8") as f:
schema = json.load(f)
sesm_schema = schema.get("properties", {}).get("sesm", {})
jsonschema.validate(instance=sesm_block, schema=sesm_schema)
if verbose:
print(" [Valid] Complies with JSON Schema draft 2020-12.")
return True, []
else:
if verbose:
print(" [Warning] Schema path not found; using structural validation fallback.")
except ImportError:
if verbose:
print(" [Warning] jsonschema library not found; using structural validation fallback.")
except Exception as e:
return False, [f"JSON Schema error: {e}"]
# Fallback manual validation
errors = []
if not isinstance(sesm_block, dict):
return False, ["SESM block must be a JSON object"]
if sesm_block.get("sesm_version") not in {"0.2.0", "0.3.0"}:
errors.append(f"Invalid or missing sesm_version (expected '0.2.0' or '0.3.0', got '{sesm_block.get('sesm_version')}')")
if "asset" in sesm_block:
asset = sesm_block["asset"]
if not isinstance(asset, dict):
errors.append("asset must be an object")
else:
if "id" not in asset:
errors.append("asset.id is required")
if "role" not in asset:
errors.append("asset.role is required")
elif asset["role"] not in [
"logo", "icon", "theme-board", "dataset-card", "dataset-header",
"pipeline-panel", "stats-panel", "navigation-card", "graph-node",
"graph-panel", "status-badge", "download-card", "schema-card",
"archive-summary", "landing-hero", "page-header", "decorative", "unknown"
]:
errors.append(f"Unknown asset.role: {asset['role']}")
if "theme" in sesm_block:
theme = sesm_block["theme"]
if not isinstance(theme, dict):
errors.append("theme must be an object")
else:
if "mode" in theme and theme["mode"] not in ["light", "dark"]:
errors.append("theme.mode must be 'light' or 'dark'")
if "accent" in theme:
accent = theme["accent"]
if not isinstance(accent, dict):
errors.append("theme.accent must be an object")
elif "hex" in accent:
if not re.match(r"^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$", accent["hex"]):
errors.append(f"Invalid theme.accent.hex color format: {accent['hex']}")
if "state" in theme:
state = theme["state"]
if not isinstance(state, dict):
errors.append("theme.state must be an object")
elif "name" in state and state["name"] not in [
"idle", "running", "active", "featured", "complete", "verified",
"warning", "error", "archived", "experimental", "deprecated"
]:
errors.append(f"Unknown theme.state.name: {state['name']}")
if "provenance" in sesm_block:
prov = sesm_block["provenance"]
if not isinstance(prov, dict):
errors.append("provenance must be an object")
else:
if "generated" in prov and not isinstance(prov["generated"], bool):
errors.append("provenance.generated must be a boolean")
return len(errors) == 0, errors
def extract_sesm_block(path: Path):
try:
tree = ET.parse(path)
root = tree.getroot()
for child in root:
tag = child.tag.split("}")[-1]
if tag == "metadata" and child.get("id") == "sesm":
text = (child.text or "").strip()
if text.startswith("<![CDATA["):
text = text[len("<![CDATA["):]
if text.endswith("]]>"):
text = text[:-len("]]>")]
text = text.strip()
return json.loads(text)
except Exception:
return None
return None
def process_svg(path: Path, overrides, schema_path: Path, verbose=False):
# 1. Read raw SVG file first (for color parsing and metadata preservation)
with open(path, "r", encoding="utf-8") as f:
svg_text = f.read()
tree = ET.parse(path)
root = tree.getroot()
slug = path.stem
# Remove existing metadata blocks
for child in list(root):
if isinstance(child.tag, str) and child.tag.split("}")[-1] == "metadata":
root.remove(child)
# Clean editor junk
strip_editor_junk(root)
# Handle viewBox and normalize dimensions
viewbox = get_viewbox(root)
root.set("viewBox", viewbox)
root.set("width", str(TARGET_SIZE))
root.set("height", str(TARGET_SIZE))
# Construct default SESM block
timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat().replace("+00:00", "Z")
derived_title = slug.replace("apt-", "").replace("-logo", "").replace(".", " ").title() + " Logo"
derived_ecosystem = slug.replace("apt-", "").replace("-logo", "").split(".")[0]
sesm_block = {
"sesm_version": "0.3.0",
"asset": {
"id": slug,
"role": "logo",
"title": derived_title,
"ecosystem": derived_ecosystem,
"tags": [derived_ecosystem, "logo", "branding"]
},
"ui": {
"dimensions": {
"width": float(TARGET_SIZE),
"height": float(TARGET_SIZE),
"viewBox": viewbox
}
},
"provenance": {
"generated": True,
"generator": {
"name": GENERATOR,
"version": "0.2.0",
"language": "python"
},
"generated_at": timestamp
}
}
# Run NIPC theme detection heuristics
detected_theme = detect_nipc_theme(svg_text)
if detected_theme:
sesm_block["theme"] = detected_theme
if verbose:
print(f" Detected NeonInk palette contract colors. Theme metadata auto-populated.")
# Apply override if available (deep merging)
override = overrides.get(slug) or overrides.get(path.name)
if override:
if "sesm" in override:
sesm_block = deep_merge(sesm_block, override["sesm"])
# Legacy mappings (ai summary/tags)
if "ai" in override and isinstance(override["ai"], dict):
ai = override["ai"]
if "summary" in ai:
if "llm" not in sesm_block:
sesm_block["llm"] = {}
if "summary" not in sesm_block["llm"]:
sesm_block["llm"]["summary"] = ai["summary"]
if "tags" in ai and isinstance(ai["tags"], list):
seen = set(sesm_block["asset"].get("tags", []))
merged_tags = list(sesm_block["asset"].get("tags", []))
for t in ai["tags"]:
if t not in seen:
merged_tags.append(t)
seen.add(t)
sesm_block["asset"]["tags"] = merged_tags
# Validate the built block
valid, errs = validate_sesm_block(sesm_block, schema_path, verbose)
if not valid:
print(f" [Validation Error] {path.name} failed verification:")
for err in errs:
print(f" - {err}")
# Inject placeholder for CDATA
metadata = ET.Element("metadata", id="sesm")
metadata.text = "__SESM_CDATA_PLACEHOLDER__"
root.insert(0, metadata)
# Convert XML to string
xml_str = ET.tostring(root, encoding="unicode")
# Replace placeholder with standard CDATA format
cdata_str = f"<![CDATA[\n{json.dumps(sesm_block, indent=2)}\n]]>"
xml_str = xml_str.replace("__SESM_CDATA_PLACEHOLDER__", cdata_str)
# Write SVG file back
with open(path, "w", encoding="utf-8") as f:
f.write('<?xml version="1.0" encoding="utf-8"?>\n' + xml_str + '\n')
return valid
def main(argv=None):
parser = argparse.ArgumentParser(description="Embed structured semantic metadata inside SVG assets.")
parser.add_argument("--input-dir", "-i", default=str(Path(__file__).parent / "svg"), help="Directory containing SVG assets.")
parser.add_argument("--overrides", "-o", default=str(Path(__file__).parent / "svg-metadata.overrides.json"), help="Path to metadata overrides file.")
parser.add_argument("--schema", "-s", default=str(Path(__file__).parent / "svg_asset.schema.json"), help="Path to validation JSON Schema.")
parser.add_argument("--validate-only", action="store_true", help="Only validate existing embedded metadata, without modifications.")
parser.add_argument("--verbose", "-v", action="store_true", help="Enable detailed logging output.")
args = parser.parse_args(argv)
svg_dir = Path(args.input_dir)
overrides_path = Path(args.overrides)
schema_path = Path(args.schema)
if not svg_dir.exists():
print(f"Error: Directory {svg_dir} does not exist.")
return 1
if args.validate_only:
print(f"Validating SVGs in {svg_dir}...")
all_valid = True
svg_files = list(svg_dir.glob("*.svg"))
if not svg_files:
print("No SVG files found.")
return 0
for svg in svg_files:
block = extract_sesm_block(svg)
if not block:
print(f"{svg.name}: [FAIL] Missing or invalid <metadata id=\"sesm\"> block.")
all_valid = False
continue
valid, errs = validate_sesm_block(block, schema_path, args.verbose)
if valid:
if args.verbose:
print(f"{svg.name}: [OK]")
else:
print(f"{svg.name}: [FAIL]")
for e in errs:
print(f" - {e}")
all_valid = False
if all_valid:
print("All assets validated successfully.")
return 0
else:
print("Some assets failed validation.")
return 2
overrides = load_overrides(overrides_path, args.verbose)
print(f"Processing and embedding metadata in {svg_dir}...")
svg_files = list(svg_dir.glob("*.svg"))
if not svg_files:
print("No SVG files found.")
return 0
failed_count = 0
for svg in svg_files:
if args.verbose:
print(f"Processing {svg.name}...")
valid = process_svg(svg, overrides, schema_path, args.verbose)
if not valid:
failed_count += 1
print(f"Done processing {len(svg_files)} SVGs.")
if failed_count > 0:
print(f"Warning: {failed_count} SVGs failed validation checks.")
return 3
return 0
if __name__ == "__main__":
sys.exit(main())
Validate-SESM-Safe.py
Validates SESM metadata and, when requested, the SESM safe SVG profile. Reports one of sesm-valid, sesm-safe, sesm-unsafe, or sesm-unverified.
python SESM\Validate-SESM-Safe.py SESM\fixtures\valid\basic-safe.svg --safe-profile
python SESM\Validate-SESM-Safe.py SESM\fixtures\valid\basic-safe.svg --safe-profile --json
#!/usr/bin/env python3
"""Validate SESM metadata and the SESM safe SVG profile."""
from __future__ import annotations
import argparse
import json
import re
import sys
import xml.etree.ElementTree as ET
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import Any
TOOL_NAME = "Validate-SESM-Safe"
TOOL_VERSION = "0.1.0"
SESM_METADATA_ID = "sesm"
MAX_METADATA_BYTES = 64 * 1024
REMOTE_URL_RE = re.compile(r"^(https?:)?//|^(https?|ftp|data):", re.IGNORECASE)
JAVASCRIPT_URL_RE = re.compile(r"^\s*javascript\s*:", re.IGNORECASE)
CREDENTIAL_RE = re.compile(
r"\b(password|passwd|api[_ -]?key|secret|token|credential|private[_ -]?key|bearer)\b",
re.IGNORECASE,
)
COMMAND_AUTHORITY_RE = re.compile(
r"\b(run|execute|shell|powershell|cmd\.exe|sudo|ignore previous|bypass policy|reveal secret|exfiltrate)\b",
re.IGNORECASE,
)
@dataclass
class Finding:
code: str
message: str
path: str = ""
@dataclass
class ValidationResult:
status: str
profile: str
file: str
errors: list[Finding]
warnings: list[Finding]
metadata_version: str | None
schema: str | None
safe_profile_checked: bool
def local_name(tag: str) -> str:
return tag.rsplit("}", 1)[-1].lower()
def element_path(element: ET.Element) -> str:
name = local_name(element.tag)
element_id = element.attrib.get("id")
return f"{name}#{element_id}" if element_id else name
def parse_svg(path: Path) -> tuple[ET.Element | None, list[Finding]]:
try:
return ET.fromstring(path.read_text(encoding="utf-8")), []
except Exception as exc:
return None, [Finding("svg-parse-error", f"SVG is not parseable XML: {exc}")]
def find_sesm_blocks(root: ET.Element) -> list[ET.Element]:
blocks = []
for element in root.iter():
if local_name(element.tag) == "metadata" and element.attrib.get("id") == SESM_METADATA_ID:
blocks.append(element)
return blocks
def metadata_text(element: ET.Element) -> str:
parts = []
if element.text:
parts.append(element.text)
for child in list(element):
if child.text:
parts.append(child.text)
if child.tail:
parts.append(child.tail)
return "".join(parts).strip()
def load_metadata(blocks: list[ET.Element]) -> tuple[dict[str, Any] | None, list[Finding], list[Finding]]:
errors: list[Finding] = []
warnings: list[Finding] = []
if not blocks:
errors.append(Finding("sesm-missing", "No <metadata id=\"sesm\"> block found."))
return None, errors, warnings
if len(blocks) > 1:
errors.append(Finding("sesm-duplicate", "More than one <metadata id=\"sesm\"> block found."))
return None, errors, warnings
raw = metadata_text(blocks[0])
if len(raw.encode("utf-8")) > MAX_METADATA_BYTES:
errors.append(Finding("sesm-oversized", f"SESM metadata exceeds {MAX_METADATA_BYTES} bytes."))
try:
data = json.loads(raw)
except Exception as exc:
errors.append(Finding("sesm-json-invalid", f"SESM metadata is not valid JSON: {exc}"))
return None, errors, warnings
if not isinstance(data, dict):
errors.append(Finding("sesm-not-object", "SESM metadata must be a JSON object."))
return None, errors, warnings
if "sesm_version" not in data:
errors.append(Finding("sesm-version-missing", "SESM metadata must include sesm_version."))
return data, errors, warnings
def validate_schema(data: dict[str, Any], schema_path: Path | None) -> list[Finding]:
if schema_path is None:
return []
if not schema_path.exists():
return [Finding("schema-missing", f"Schema not found: {schema_path}")]
try:
schema = json.loads(schema_path.read_text(encoding="utf-8"))
except Exception as exc:
return [Finding("schema-invalid", f"Schema is not valid JSON: {exc}", str(schema_path))]
try:
import jsonschema # type: ignore
except Exception:
return structural_validate(data)
wrapper = {
"type": "asset",
"asset_type": "svg",
"slug": data.get("asset", {}).get("id", "sesm-asset"),
"source": {"format": "svg", "content": ""},
"sesm": data,
}
validator = jsonschema.Draft202012Validator(schema)
findings = []
for error in sorted(validator.iter_errors(wrapper), key=lambda item: list(item.path)):
path = ".".join(str(part) for part in error.absolute_path)
findings.append(Finding("schema-validation", error.message, path))
return findings
def structural_validate(data: dict[str, Any]) -> list[Finding]:
findings = []
version = data.get("sesm_version")
if version not in {"0.2.0", "0.3.0"}:
findings.append(Finding("schema-version", "sesm_version must be 0.2.0 or 0.3.0.", "sesm_version"))
asset = data.get("asset")
if asset is not None:
if not isinstance(asset, dict):
findings.append(Finding("schema-asset", "asset must be an object.", "asset"))
else:
if not asset.get("id"):
findings.append(Finding("schema-asset-id", "asset.id is recommended for validation.", "asset.id"))
if not asset.get("role"):
findings.append(Finding("schema-asset-role", "asset.role is required when asset is present.", "asset.role"))
return findings
def check_safe_profile(root: ET.Element, data: dict[str, Any] | None) -> tuple[list[Finding], list[Finding]]:
errors: list[Finding] = []
warnings: list[Finding] = []
for element in root.iter():
name = local_name(element.tag)
path = element_path(element)
if name == "script":
errors.append(Finding("svg-script", "SESM-safe SVGs must not contain <script>.", path))
if name == "text":
style = element.attrib.get("style", "").lower()
hidden = (
element.attrib.get("display") == "none"
or element.attrib.get("visibility") == "hidden"
or "display:none" in style.replace(" ", "")
or "visibility:hidden" in style.replace(" ", "")
or "opacity:0" in style.replace(" ", "")
)
text = "".join(element.itertext()).strip()
if hidden and text:
warnings.append(Finding("hidden-text", "Hidden text with content should be reviewed.", path))
for attr, value in element.attrib.items():
attr_name = local_name(attr)
value_text = str(value)
if attr_name.startswith("on"):
errors.append(Finding("svg-event-handler", f"Event handler attribute is forbidden: {attr_name}", path))
if JAVASCRIPT_URL_RE.match(value_text):
errors.append(Finding("javascript-url", "javascript: URLs are forbidden.", path))
if attr_name in {"href", "src", "xlink:href"} or attr.endswith("}href"):
if REMOTE_URL_RE.match(value_text):
warnings.append(Finding("remote-reference", f"Remote reference should be reviewed: {value_text}", path))
elif REMOTE_URL_RE.search(value_text):
warnings.append(Finding("remote-reference", f"Remote URL should be reviewed: {value_text}", path))
if data is not None:
data_text = json.dumps(data, sort_keys=True)
if CREDENTIAL_RE.search(data_text):
errors.append(Finding("credential-request", "SESM metadata appears to mention credentials or secrets."))
if COMMAND_AUTHORITY_RE.search(data_text):
errors.append(Finding("agent-command-authority", "SESM metadata appears to request command authority or policy bypass."))
return errors, warnings
def validate_file(path: Path, schema_path: Path | None, safe_profile: bool) -> ValidationResult:
errors: list[Finding] = []
warnings: list[Finding] = []
if not path.exists():
return ValidationResult(
status="error",
profile="sesm-unverified",
file=str(path),
errors=[Finding("input-missing", f"Input file not found: {path}")],
warnings=[],
metadata_version=None,
schema=str(schema_path) if schema_path else None,
safe_profile_checked=safe_profile,
)
root, parse_errors = parse_svg(path)
errors.extend(parse_errors)
metadata_version = None
if root is None:
return ValidationResult("error", "sesm-unverified", str(path), errors, warnings, None, str(schema_path) if schema_path else None, safe_profile)
metadata, metadata_errors, metadata_warnings = load_metadata(find_sesm_blocks(root))
errors.extend(metadata_errors)
warnings.extend(metadata_warnings)
if metadata is not None:
metadata_version = str(metadata.get("sesm_version")) if metadata.get("sesm_version") is not None else None
errors.extend(validate_schema(metadata, schema_path))
if safe_profile:
safe_errors, safe_warnings = check_safe_profile(root, metadata)
errors.extend(safe_errors)
warnings.extend(safe_warnings)
if errors:
profile = "sesm-unsafe" if safe_profile else "sesm-unverified"
status = "error"
elif safe_profile and warnings:
profile = "sesm-unverified"
status = "warning"
elif safe_profile:
profile = "sesm-safe"
status = "ok"
else:
profile = "sesm-valid"
status = "ok" if not warnings else "warning"
return ValidationResult(status, profile, str(path), errors, warnings, metadata_version, str(schema_path) if schema_path else None, safe_profile)
def print_human(result: ValidationResult) -> None:
print(f"{TOOL_NAME} {TOOL_VERSION}")
print(f"File: {result.file}")
print(f"Status: {result.status}")
print(f"Profile: {result.profile}")
if result.metadata_version:
print(f"SESM version: {result.metadata_version}")
for finding in result.errors:
location = f" ({finding.path})" if finding.path else ""
print(f"ERROR {finding.code}{location}: {finding.message}")
for finding in result.warnings:
location = f" ({finding.path})" if finding.path else ""
print(f"WARN {finding.code}{location}: {finding.message}")
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("svg", type=Path, help="SVG file to validate.")
parser.add_argument("--schema", type=Path, default=Path(__file__).with_name("svg_asset.schema.json"))
parser.add_argument("--safe-profile", action="store_true", help="Check the SESM safe SVG profile.")
parser.add_argument("--json", action="store_true", help="Emit JSON output.")
args = parser.parse_args()
result = validate_file(args.svg, args.schema, args.safe_profile)
if args.json:
print(json.dumps(asdict(result), indent=2))
else:
print_human(result)
if result.status == "ok":
return 0
if result.status == "warning":
return 0
return 4 if any(error.code != "input-missing" for error in result.errors) else 3
if __name__ == "__main__":
raise SystemExit(main())
Convert-to-SVG.py
Converts traditional raster formats (PNG, JPG, WEBP, BMP, etc.) into clean SVGs, either by embedding the raster as base64 (default, guarantees identical appearance) or by monochrome vector tracing with OpenCV contours (--mode trace, with hole-carving via RETR_CCOMP so letterforms like "O" and "A" render correctly).
CLI usage:
python Convert-to-SVG.py --input "path/to/image" [options]
Key options: --mode {auto,embed,trace}, --threshold (integer or auto for Otsu thresholding), --simplify (Ramer-Douglas-Peucker epsilon), --invert, --blur (Gaussian blur kernel size), --min-area (noise contour filter), --overwrite. If OpenCV is unavailable or tracing finds no contours, the tool falls back to base64 embedding automatically.
# =========================================================
# Script Name: Convert-to-SVG.py
# Description: Convert images (PNG, JPG, WEBP, etc.) to SVG.
# Default: embed raster inside SVG as base64 (preserves appearance).
# Optional: monochrome vector tracing using OpenCV contours if available.
# Author: APTlantis Team
# Creation Date: 2025-08-20
# Last Modified: 2026-05-27
#
# Dependencies:
# - Python 3.x
# - Pillow (PIL)
# - Optional: opencv-python (for trace mode)
#
# Usage examples:
# python Convert-to-SVG.py --input "C:\\path\\to\\image.png"
# python Convert-to-SVG.py --input "C:\\Users\\Administrator\\Desktop\\AptlantisLogos\\png" --mode embed
# python Convert-to-SVG.py --input "C:\\path\\to\\image.jpg" --mode trace --threshold auto --simplify 2.5 --blur 3 --min-area 15
# python Convert-to-SVG.py --input "C:\\path\\to\\image.webp" --mode embed --output-dir "C:\\out"
# =========================================================
import os
import io
import sys
import base64
import argparse
from typing import Optional, Tuple, Union
try:
from PIL import Image # type: ignore
_PIL_AVAILABLE = True
except Exception:
Image = None # type: ignore
_PIL_AVAILABLE = False
# Optional import for tracing
try:
import cv2 # type: ignore
_CV2_AVAILABLE = True
except Exception:
_CV2_AVAILABLE = False
SUPPORTED_EXTS = {'.png', '.jpg', '.jpeg', '.webp', '.bmp', '.gif', '.tif', '.tiff'}
def ensure_dir(path: str) -> None:
if path and not os.path.isdir(path):
os.makedirs(path, exist_ok=True)
def make_output_path(input_path: str, output_dir: Optional[str]) -> str:
base, _ = os.path.splitext(os.path.basename(input_path))
out_dir = output_dir if output_dir else os.path.dirname(input_path)
ensure_dir(out_dir)
return os.path.join(out_dir, base + '.svg')
def pil_load_rgba(path: str) -> Image.Image:
# Use PIL to load first frame; convert to RGBA for consistent embedding
if not _PIL_AVAILABLE:
raise RuntimeError("Pillow is required for embedding raster images. Install with: pip install Pillow")
try:
img = Image.open(path)
if getattr(img, "is_animated", False):
img.seek(0)
if img.mode not in ("RGBA", "RGB"):
img = img.convert("RGBA")
return img
except Exception as e:
raise RuntimeError(f"Pillow failed to load image: {e}")
def image_to_svg_embed(input_path: str, output_path: str, png_compress_level: int = 6) -> None:
# Load image, encode as PNG in-memory, embed into SVG as base64
img = pil_load_rgba(input_path)
width, height = img.size
buf = io.BytesIO()
# Save as PNG to preserve transparency uniformly
img.save(buf, format='PNG', compress_level=png_compress_level)
data_b64 = base64.b64encode(buf.getvalue()).decode('ascii')
svg = []
svg.append('<?xml version="1.0" encoding="UTF-8" standalone="no"?>')
svg.append('<svg xmlns="http://www.w3.org/2000/svg" \n xmlns:xlink="http://www.w3.org/1999/xlink" \n version="1.1" width="%d" height="%d" viewBox="0 0 %d %d">' % (width, height, width, height))
svg.append(' <image x="0" y="0" width="%d" height="%d" xlink:href="data:image/png;base64,%s"/>' % (width, height, data_b64))
svg.append('</svg>')
with open(output_path, 'w', encoding='utf-8') as f:
f.write("\n".join(svg))
def _threshold_image(gray: 'cv2.Mat', threshold: str | int) -> Tuple['cv2.Mat', int]: # type: ignore
# Helper: apply threshold; supports 'auto' (Otsu) or specific integer
if isinstance(threshold, str):
thr_str = threshold.strip().lower()
if thr_str == 'auto':
# Otsu's method
_, bw = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
return bw, 0
else:
try:
tval = int(threshold)
except Exception:
tval = 128
else:
tval = int(threshold)
_, bw = cv2.threshold(gray, tval, 255, cv2.THRESH_BINARY)
return bw, tval
def image_to_svg_trace(
input_path: str,
output_path: str,
threshold: str | int = 'auto',
simplify: float = 2.0,
invert: bool = False,
fill_color: str = '#000000',
blur: int = 0,
min_area: float = 10.0
) -> None:
if not _CV2_AVAILABLE:
raise RuntimeError("Trace mode requires opencv-python. Install with: pip install opencv-python")
# Read with OpenCV
img = cv2.imread(input_path, cv2.IMREAD_UNCHANGED)
if img is None:
raise RuntimeError(f"Failed to read image: {input_path}")
height, width = img.shape[:2]
# Build grayscale; respect alpha if present by premultiplying over white
if img.ndim == 3 and img.shape[2] == 4: # BGRA
b, g, r, a = cv2.split(img)
alpha = a.astype('float32') / 255.0
rgb = cv2.merge([b, g, r]).astype('float32')
white = 255.0
composite = (rgb * alpha[..., None] + white * (1.0 - alpha[..., None])).astype('uint8')
gray = cv2.cvtColor(composite, cv2.COLOR_BGR2GRAY)
elif img.ndim == 3:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
else:
gray = img
# Apply Gaussian Blur if specified
if blur > 0:
if blur % 2 == 0:
blur += 1 # Kernel size must be odd
gray = cv2.GaussianBlur(gray, (blur, blur), 0)
bw, used_threshold = _threshold_image(gray, threshold)
if invert:
bw = 255 - bw
# Find contours using RETR_CCOMP to extract parent contours and their internal holes
contours, hierarchy = cv2.findContours(bw, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
path_elements = []
if hierarchy is not None and len(contours) > 0:
hierarchy_flat = hierarchy[0]
for i in range(len(contours)):
# If contour is a parent (external boundary, parent_idx == -1)
if hierarchy_flat[i][3] == -1:
# Check minimum area
if cv2.contourArea(contours[i]) < min_area:
continue
# Simplify parent contour
approx = cv2.approxPolyDP(contours[i], simplify, True) if simplify > 0 else contours[i]
if len(approx) < 3:
continue
# Construct SVG path definition
d_parts = []
pts = approx.reshape(-1, 2)
x0, y0 = pts[0]
d_parts.append(f"M {x0} {y0}")
for x, y in pts[1:]:
d_parts.append(f"L {x} {y}")
d_parts.append("Z")
# Traversal through children of this parent contour (holes)
child_idx = hierarchy_flat[i][2]
while child_idx != -1:
# Filter child contour by minimum area
if cv2.contourArea(contours[child_idx]) >= min_area:
approx_child = cv2.approxPolyDP(contours[child_idx], simplify, True) if simplify > 0 else contours[child_idx]
if len(approx_child) >= 3:
cpts = approx_child.reshape(-1, 2)
cx0, cy0 = cpts[0]
d_parts.append(f"M {cx0} {cy0}")
for cx, cy in cpts[1:]:
d_parts.append(f"L {cx} {cy}")
d_parts.append("Z")
# Move to next child contour sibling
child_idx = hierarchy_flat[child_idx][0]
d_attr = " ".join(d_parts)
path_elements.append(f'<path d="{d_attr}" fill="{fill_color}" fill-rule="evenodd" stroke="none"/>')
svg_lines = []
svg_lines.append('<?xml version="1.0" encoding="UTF-8" standalone="no"?>')
svg_lines.append('<svg xmlns="http://www.w3.org/2000/svg" \n xmlns:xlink="http://www.w3.org/1999/xlink" \n version="1.1" width="%d" height="%d" viewBox="0 0 %d %d">' % (width, height, width, height))
if not path_elements:
# Fallback: if no contours detected, embed raster instead of saving an empty SVG
img_pil = pil_load_rgba(input_path)
buf = io.BytesIO()
img_pil.save(buf, format='PNG', compress_level=6)
data_b64 = base64.b64encode(buf.getvalue()).decode('ascii')
svg_lines.append(' <!-- No contours detected; embedding raster fallback -->')
svg_lines.append(' <image x="0" y="0" width="%d" height="%d" xlink:href="data:image/png;base64,%s"/>' % (width, height, data_b64))
else:
svg_lines.extend(" " + p for p in path_elements)
svg_lines.append('</svg>')
with open(output_path, 'w', encoding='utf-8') as f:
f.write("\n".join(svg_lines))
def process_path(
input_path: str,
output_dir: Optional[str],
mode: str,
threshold: str | int,
simplify: float,
invert: bool,
overwrite: bool,
blur: int = 0,
min_area: float = 10.0
) -> int:
if os.path.isfile(input_path):
ext = os.path.splitext(input_path)[1].lower()
if ext not in SUPPORTED_EXTS:
print(f"Skipping unsupported file: {input_path}")
return 0
out_path = make_output_path(input_path, output_dir)
if (not overwrite) and os.path.exists(out_path):
print(f"Skipping (exists): {out_path}")
return 0
run_conversion(input_path, out_path, mode, threshold, simplify, invert, blur, min_area)
print(f"Converted: {input_path} -> {out_path}")
return 1
# Directory processing
count = 0
for root, _, files in os.walk(input_path):
for fname in files:
ext = os.path.splitext(fname)[1].lower()
if ext not in SUPPORTED_EXTS:
continue
src = os.path.join(root, fname)
out_path = make_output_path(src, output_dir)
if (not overwrite) and os.path.exists(out_path):
print(f"Skipping (exists): {out_path}")
continue
run_conversion(src, out_path, mode, threshold, simplify, invert, blur, min_area)
print(f"Converted: {src} -> {out_path}")
count += 1
return count
def run_conversion(
src: str,
dst: str,
mode: str,
threshold: str | int,
simplify: float,
invert: bool,
blur: int = 0,
min_area: float = 10.0
) -> None:
chosen_mode = mode.lower()
if chosen_mode == 'auto':
chosen_mode = 'trace' if _CV2_AVAILABLE else 'embed'
if chosen_mode == 'embed':
image_to_svg_embed(src, dst)
elif chosen_mode == 'trace':
image_to_svg_trace(src, dst, threshold=threshold, simplify=simplify, invert=invert, blur=blur, min_area=min_area)
else:
raise ValueError("Invalid mode. Use one of: auto, embed, trace")
def parse_args(argv=None):
parser = argparse.ArgumentParser(description='Convert images (PNG/JPG/WEBP/etc.) to SVG. Default embeds the raster; optional trace mode vectorizes (monochrome).')
parser.add_argument('--input', required=True, help='Input file or directory')
parser.add_argument('--output-dir', default=None, help='Output directory (defaults to alongside source files)')
parser.add_argument('--mode', choices=['auto', 'embed', 'trace'], default='auto', help='Conversion mode: auto (trace if OpenCV present, else embed), embed (base64 image), trace (vector contours)')
parser.add_argument('--threshold', default='auto', help='Threshold for trace: integer [0-255] or "auto" (Otsu)')
parser.add_argument('--simplify', type=float, default=2.0, help='Polygon simplification epsilon for trace (pixels). Use 0 to disable.')
parser.add_argument('--invert', action='store_true', help='Invert the thresholded bitmap before tracing')
parser.add_argument('--overwrite', action='store_true', help='Overwrite existing .svg files')
parser.add_argument('--blur', type=int, default=0, help='Gaussian blur kernel size (odd integer). Use 0 to disable.')
parser.add_argument('--min-area', type=float, default=10.0, help='Minimum contour area (pixels) to include. Default is 10.0.')
return parser.parse_args(argv)
def main(argv=None):
args = parse_args(argv)
in_path = os.path.expandvars(os.path.expanduser(args.input))
out_dir = os.path.expandvars(os.path.expanduser(args.output_dir)) if args.output_dir else None
if not os.path.exists(in_path):
print(f"Error: input path not found: {in_path}")
return 1
try:
cnt = process_path(
in_path,
out_dir,
args.mode,
args.threshold,
args.simplify,
args.invert,
args.overwrite,
args.blur,
args.min_area
)
print(f"Done. Created {cnt} SVG file(s).")
return 0
except Exception as e:
print(f"Error: {e}")
return 2
if __name__ == '__main__':
sys.exit(main())
Test Suite
tests/run_tests.py discovers and runs every test_*.py file under tests/ using Python's standard unittest framework:
import unittest
import sys
from pathlib import Path
def main():
# Resolve the start directory relative to this script
start_dir = str(Path(__file__).parent)
print("Discovering and running tests under tests/...")
loader = unittest.TestLoader()
suite = loader.discover(start_dir=start_dir, pattern='test_*.py')
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
if not result.wasSuccessful():
print("Test suite failed.")
sys.exit(1)
print("Test suite completed successfully.")
sys.exit(0)
if __name__ == '__main__':
main()
Coverage across the three test_*.py modules (full source not reproduced here — see tests/ in the standard directory):
test_embed_sesm.py— deep-merge behavior, NIPC theme-detection heuristics (color-to-token mapping, accent/state derivation), manual schema-fallback validation (valid and invalid blocks), and end-to-endprocess_svgbehavior including legacyai.summary/ai.tagsmapping.test_convert_to_svg.py— base64 raster embedding, OpenCV vector tracing with hole-carving (verifyingfill-rule="evenodd"and correct sub-path counts for shapes with holes), minimum-area noise filtering, and the trace-to-embed fallback when OpenCV is unavailable.test_validate_sesm_safe.py— runsvalidate_fileagainst every fixture infixtures/valid,fixtures/invalid, andfixtures/warning, asserting the exact status/profile/error-code each fixture is supposed to produce (see Fixtures).