Skip to main content
Glama
SVGExporter.ts4.76 kB
/** * SVG Exporter * Server-side SVG extraction and metadata injection * * Purpose: Pre-process SVG artifacts for enhanced export * Use Cases: Add metadata, optimize viewBox, sanitize attributes */ import { ArtifactMetadata } from '../types.js'; /** * Handles server-side SVG extraction and metadata enrichment */ export class SVGExporter { /** * Extract SVG element from HTML content * Used for server-side SVG pre-processing * * @param htmlContent - Full HTML artifact content * @returns SVG element string or null if not found */ extractSVG(htmlContent: string): string | null { // Match SVG element with all nested content const svgMatch = htmlContent.match(/<svg[^>]*>[\s\S]*?<\/svg>/i); return svgMatch ? svgMatch[0] : null; } /** * Add RDF metadata to SVG element * Injects metadata as <metadata> element with RDF/Dublin Core * * @param svgContent - SVG element string * @param metadata - Artifact metadata * @returns SVG with embedded metadata */ addMetadata(svgContent: string, metadata: Partial<ArtifactMetadata>): string { const title = metadata.title || 'CTS Artifact'; const description = metadata.description || 'Generated by CTS MCP Server'; const version = '3.2.0'; // CTS MCP Server version const timestamp = new Date().toISOString(); const metadataXML = ` <metadata> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <rdf:Description rdf:about=""> <dc:title>${this.escapeXML(title)}</dc:title> <dc:description>${this.escapeXML(description)}</dc:description> <dc:creator>CTS MCP Server v${this.escapeXML(version)}</dc:creator> <dc:date>${timestamp}</dc:date> <dc:format>image/svg+xml</dc:format> </rdf:Description> </rdf:RDF> </metadata>`; // Insert metadata after opening <svg> tag return svgContent.replace(/(<svg[^>]*>)/, `$1${metadataXML}`); } /** * Optimize SVG viewBox for export * Calculates bounding box and sets viewBox attribute * * @param svgContent - SVG element string * @returns SVG with optimized viewBox */ optimizeViewBox(svgContent: string): string { // Check if viewBox already exists if (/viewBox=/.test(svgContent)) { return svgContent; } // Extract width and height attributes const widthMatch = svgContent.match(/width="(\d+)"/); const heightMatch = svgContent.match(/height="(\d+)"/); if (widthMatch && heightMatch) { const width = widthMatch[1]; const height = heightMatch[1]; const viewBox = `viewBox="0 0 ${width} ${height}"`; // Add viewBox attribute return svgContent.replace(/(<svg[^>]*)>/, `$1 ${viewBox}>`); } return svgContent; } /** * Ensure SVG has proper XML namespace * Adds xmlns attribute if missing * * @param svgContent - SVG element string * @returns SVG with xmlns attribute */ ensureNamespace(svgContent: string): string { if (/xmlns=/.test(svgContent)) { return svgContent; } return svgContent.replace(/(<svg)/, '$1 xmlns="http://www.w3.org/2000/svg"'); } /** * Process SVG for export * Applies all optimizations and metadata in one pass * * @param svgContent - Raw SVG content * @param metadata - Artifact metadata * @returns Processed SVG ready for export */ processSVGForExport(svgContent: string, metadata: Partial<ArtifactMetadata>): string { let processed = svgContent; // Ensure namespace processed = this.ensureNamespace(processed); // Optimize viewBox processed = this.optimizeViewBox(processed); // Add metadata processed = this.addMetadata(processed, metadata); return processed; } /** * Extract SVG from HTML and process for export * Convenience method combining extraction and processing * * @param htmlContent - Full HTML artifact * @param metadata - Artifact metadata * @returns Processed SVG or null if no SVG found */ extractAndProcess(htmlContent: string, metadata: Partial<ArtifactMetadata>): string | null { const svg = this.extractSVG(htmlContent); if (!svg) { return null; } return this.processSVGForExport(svg, metadata); } /** * Escape XML special characters * Prevents XML injection in metadata * * @param text - Text to escape * @returns Escaped text safe for XML */ private escapeXML(text: string): string { return text .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&apos;'); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/EricA1019/CTS_MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server