// ==UserScript== // @name 带预览和下载功能的 SVG 提取器(GlyphWiki 特别版) // @license MIT // @namespace http://tampermonkey.net/ // @version 1.9 // @description 提取页面中的所有 SVG,提供预览并选择下载 SVG 或 PNG(PNG 尺寸根据 SVG 长宽比例调整),增加预览区关闭功能,支持外部 SVG 文件,特别处理 GlyphWiki 的 SVG 移除网格和矩形边界 // @author 般若 // @match *://*/* // @grant none // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 创建预览容器 const previewContainer = document.createElement('div'); previewContainer.style.position = 'fixed'; previewContainer.style.bottom = '20px'; previewContainer.style.right = '20px'; previewContainer.style.zIndex = '10000'; previewContainer.style.backgroundColor = 'white'; previewContainer.style.padding = '10px'; previewContainer.style.border = '1px solid #ccc'; previewContainer.style.borderRadius = '5px'; previewContainer.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)'; previewContainer.style.maxHeight = '80vh'; previewContainer.style.overflowY = 'auto'; document.body.appendChild(previewContainer); // 创建关闭按钮 const closeButton = document.createElement('button'); closeButton.innerText = '×'; closeButton.style.position = 'absolute'; closeButton.style.top = '5px'; closeButton.style.right = '5px'; closeButton.style.backgroundColor = 'transparent'; closeButton.style.border = 'none'; closeButton.style.fontSize = '16px'; closeButton.style.cursor = 'pointer'; closeButton.addEventListener('click', () => { previewContainer.style.display = 'none'; }); previewContainer.appendChild(closeButton); // 创建提取按钮 const extractButton = document.createElement('button'); extractButton.innerText = '提取 SVG'; extractButton.style.position = 'fixed'; extractButton.style.bottom = '20px'; extractButton.style.left = '20px'; extractButton.style.zIndex = '10000'; extractButton.style.padding = '10px'; extractButton.style.backgroundColor = '#007bff'; extractButton.style.color = 'white'; extractButton.style.border = 'none'; extractButton.style.borderRadius = '5px'; extractButton.style.cursor = 'pointer'; document.body.appendChild(extractButton); // 创建关闭预览区按钮 const closePreviewButton = document.createElement('button'); closePreviewButton.innerText = '关闭预览区'; closePreviewButton.style.position = 'fixed'; closePreviewButton.style.bottom = '60px'; closePreviewButton.style.left = '20px'; closePreviewButton.style.zIndex = '10000'; closePreviewButton.style.padding = '10px'; closePreviewButton.style.backgroundColor = '#dc3545'; closePreviewButton.style.color = 'white'; closePreviewButton.style.border = 'none'; closePreviewButton.style.borderRadius = '5px'; closePreviewButton.style.cursor = 'pointer'; closePreviewButton.addEventListener('click', () => { previewContainer.style.display = 'none'; }); document.body.appendChild(closePreviewButton); // 移除 GlyphWiki SVG 的网格和矩形边界 function removeGlyphWikiBackground(svg) { if (window.location.hostname === 'glyphwiki.org') { const rects = svg.querySelectorAll('rect.glyph-boundary, rect.glyph-guide'); rects.forEach(rect => rect.remove()); const gridLines = svg.querySelectorAll('g.grid-lines'); gridLines.forEach(grid => grid.remove()); // 修改 viewBox 和尺寸 svg.setAttribute('viewBox', '0 0 200 200'); svg.setAttribute('width', '100%'); svg.setAttribute('height', '100%'); } } // 加载外部 SVG 文件并将其转换为内联 SVG async function loadAndInlineExternalSVGs() { const imgElements = Array.from(document.querySelectorAll('img[src$=".svg"]')); for (const img of imgElements) { try { const response = await fetch(img.src); const svgText = await response.text(); const parser = new DOMParser(); const svgDoc = parser.parseFromString(svgText, 'image/svg+xml'); const svgElement = svgDoc.querySelector('svg'); if (svgElement) { img.replaceWith(svgElement); removeGlyphWikiBackground(svgElement); // 移除 GlyphWiki 背景 } } catch (error) { console.error('Failed to load and inline SVG:', error); } } } // 提取页面中的所有 SVG 元素 function extractSVGs() { const svgs = Array.from(document.querySelectorAll('svg')).map((svg, index) => { removeGlyphWikiBackground(svg); // 移除 GlyphWiki 背景 const serializer = new XMLSerializer(); const svgString = serializer.serializeToString(svg); const blob = new Blob([svgString], { type: 'image/svg+xml' }); const url = URL.createObjectURL(blob); return { url, name: `svg-${index + 1}.svg`, element: svg }; }); // 特别处理字统网动态更新的 SVG const dynamicSvgs = Array.from(document.querySelectorAll('svg[id="zusvgimgkage"], svg[style*="position:absolute;left:0;top:0;"]')).map((svg, index) => { const serializer = new XMLSerializer(); const svgString = serializer.serializeToString(svg); const blob = new Blob([svgString], { type: 'image/svg+xml' }); const url = URL.createObjectURL(blob); return { url, name: `dynamic-svg-${index + 1}.svg`, element: svg }; }); return svgs.concat(dynamicSvgs); } // 显示 SVG 预览 function showSVGPreviews(svgs) { previewContainer.innerHTML = '
未找到 SVG 元素。
'; } }); })();