web_ppt / test-huggingface-vector-fix.html
CatPtain's picture
Upload 2 files
e554064 verified
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Huggingface环境矢量图形修复测试</title>
<style>
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #333;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 15px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #ff6b6b, #ee5a24);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
margin: 0;
font-size: 2.5em;
font-weight: 300;
}
.header p {
margin: 10px 0 0 0;
opacity: 0.9;
font-size: 1.1em;
}
.content {
padding: 40px;
}
.section {
margin-bottom: 40px;
padding: 25px;
border: 2px solid #f0f0f0;
border-radius: 10px;
background: #fafafa;
}
.section h2 {
color: #2c3e50;
margin-top: 0;
border-bottom: 3px solid #3498db;
padding-bottom: 10px;
}
.test-area {
display: flex;
gap: 20px;
flex-wrap: wrap;
margin: 20px 0;
}
.svg-container {
flex: 1;
min-width: 200px;
padding: 20px;
border: 2px dashed #ddd;
border-radius: 8px;
text-align: center;
background: white;
}
.svg-container h3 {
margin-top: 0;
color: #34495e;
}
.test-svg {
margin: 10px;
border: 1px solid #eee;
}
.controls {
margin: 20px 0;
text-align: center;
}
.btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 12px 25px;
margin: 5px;
border-radius: 25px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
.btn:active {
transform: translateY(0);
}
.btn.success {
background: linear-gradient(135deg, #00b894, #00a085);
}
.btn.warning {
background: linear-gradient(135deg, #fdcb6e, #e17055);
}
.btn.danger {
background: linear-gradient(135deg, #fd79a8, #e84393);
}
.results {
margin-top: 20px;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #007bff;
}
.log {
background: #2c3e50;
color: #ecf0f1;
padding: 15px;
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 14px;
max-height: 300px;
overflow-y: auto;
white-space: pre-wrap;
margin-top: 15px;
}
.environment-info {
background: linear-gradient(135deg, #74b9ff, #0984e3);
color: white;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.environment-info h3 {
margin-top: 0;
}
.fix-list {
background: #d4edda;
border: 1px solid #c3e6cb;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
.fix-item {
margin: 8px 0;
padding: 8px 12px;
background: white;
border-radius: 5px;
border-left: 4px solid #28a745;
}
.preview-container {
display: flex;
gap: 20px;
margin-top: 20px;
flex-wrap: wrap;
}
.preview-item {
flex: 1;
min-width: 300px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
background: white;
}
.preview-item h4 {
margin-top: 0;
color: #495057;
}
.preview-image {
max-width: 100%;
border: 1px solid #eee;
border-radius: 4px;
}
.status {
display: inline-block;
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
margin-left: 10px;
}
.status.success {
background: #d4edda;
color: #155724;
}
.status.error {
background: #f8d7da;
color: #721c24;
}
.status.warning {
background: #fff3cd;
color: #856404;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚀 Huggingface环境矢量图形修复测试</h1>
<p>专门针对Huggingface Spaces部署环境的矢量图形渲染优化</p>
</div>
<div class="content">
<!-- 环境信息 -->
<div class="environment-info">
<h3>🌐 环境检测</h3>
<div id="environment-status">正在检测环境...</div>
</div>
<!-- 修复说明 -->
<div class="section">
<h2>🔧 Huggingface环境修复措施</h2>
<div class="fix-list">
<div class="fix-item">✅ 禁用CORS检查,使用allowTaint模式</div>
<div class="fix-item">✅ 优化html2canvas配置,适配Docker Alpine环境</div>
<div class="fix-item">✅ 简化SVG序列化,移除problematic属性</div>
<div class="fix-item">✅ 使用btoa编码替代复杂的Base64实现</div>
<div class="fix-item">✅ 添加多级回退机制,确保渲染成功</div>
<div class="fix-item">✅ 针对Chromium浏览器优化SVG处理</div>
<div class="fix-item">✅ 禁用foreignObject渲染,避免跨域问题</div>
</div>
</div>
<!-- 测试SVG图形 -->
<div class="section">
<h2>🎨 测试SVG图形</h2>
<div class="test-area">
<div class="svg-container">
<h3>几何图形</h3>
<svg class="test-svg" width="150" height="100" id="svg1">
<rect x="10" y="10" width="60" height="40" fill="#ff6b6b" stroke="#c0392b" stroke-width="2"/>
<circle cx="100" cy="30" r="20" fill="#3498db" stroke="#2980b9" stroke-width="2"/>
<polygon points="80,60 100,80 120,60 110,90 90,90" fill="#2ecc71" stroke="#27ae60" stroke-width="2"/>
</svg>
</div>
<div class="svg-container">
<h3>路径图形</h3>
<svg class="test-svg" width="150" height="100" id="svg2">
<path d="M20,50 Q40,20 60,50 T100,50" stroke="#9b59b6" stroke-width="3" fill="none"/>
<path d="M10,80 L50,60 L90,80 L130,60" stroke="#f39c12" stroke-width="2" fill="none"/>
<ellipse cx="75" cy="30" rx="30" ry="15" fill="#e74c3c" opacity="0.7"/>
</svg>
</div>
<div class="svg-container">
<h3>文本图形</h3>
<svg class="test-svg" width="150" height="100" id="svg3">
<rect width="100%" height="100%" fill="#ecf0f1"/>
<text x="75" y="30" text-anchor="middle" font-family="Arial" font-size="14" fill="#2c3e50">SVG文本</text>
<text x="75" y="50" text-anchor="middle" font-family="Arial" font-size="12" fill="#7f8c8d">Huggingface</text>
<text x="75" y="70" text-anchor="middle" font-family="Arial" font-size="10" fill="#95a5a6">优化测试</text>
</svg>
</div>
<div class="svg-container">
<h3>复杂图形</h3>
<svg class="test-svg" width="150" height="100" id="svg4">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />
<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="100%" height="100%" fill="url(#grad1)"/>
<circle cx="75" cy="50" r="30" fill="white" opacity="0.8"/>
<text x="75" y="55" text-anchor="middle" font-family="Arial" font-size="16" fill="#333">HF</text>
</svg>
</div>
</div>
</div>
<!-- 测试控制 -->
<div class="section">
<h2>🧪 测试控制</h2>
<div class="controls">
<button class="btn" onclick="testEnvironmentDetection()">检测环境</button>
<button class="btn success" onclick="testSvgConversion()">测试SVG转换</button>
<button class="btn warning" onclick="testHuggingfaceRenderer()">测试HF渲染器</button>
<button class="btn danger" onclick="testExportFunction()">测试导出功能</button>
<button class="btn" onclick="clearResults()">清除结果</button>
</div>
</div>
<!-- 测试结果 -->
<div class="section">
<h2>📊 测试结果</h2>
<div id="test-results" class="results">
<p>点击上方按钮开始测试...</p>
</div>
<!-- 预览区域 -->
<div class="preview-container" id="preview-container" style="display: none;">
<div class="preview-item">
<h4>原始SVG</h4>
<div id="original-svg"></div>
</div>
<div class="preview-item">
<h4>转换结果</h4>
<div id="converted-result"></div>
</div>
</div>
<!-- 日志区域 -->
<div class="log" id="log-area"></div>
</div>
</div>
</div>
<script>
// 模拟Huggingface环境检测
function isHuggingfaceEnvironment() {
return (
window.location.hostname.includes('hf.space') ||
window.location.hostname.includes('huggingface.co') ||
window.location.hostname.includes('localhost') || // 本地测试
window.location.hostname.includes('127.0.0.1')
);
}
// 日志函数
function log(message, type = 'info') {
const logArea = document.getElementById('log-area');
const timestamp = new Date().toLocaleTimeString();
const logEntry = `[${timestamp}] ${type.toUpperCase()}: ${message}\n`;
logArea.textContent += logEntry;
logArea.scrollTop = logArea.scrollHeight;
console.log(logEntry);
}
// 更新结果显示
function updateResults(content) {
document.getElementById('test-results').innerHTML = content;
}
// 清除结果
function clearResults() {
document.getElementById('test-results').innerHTML = '<p>结果已清除,点击测试按钮开始新的测试...</p>';
document.getElementById('log-area').textContent = '';
document.getElementById('preview-container').style.display = 'none';
}
// 测试环境检测
function testEnvironmentDetection() {
log('开始环境检测测试');
const isHF = isHuggingfaceEnvironment();
const userAgent = navigator.userAgent;
const hostname = window.location.hostname;
const envInfo = {
isHuggingface: isHF,
hostname: hostname,
userAgent: userAgent,
isChromium: userAgent.includes('Chrome'),
isDocker: userAgent.includes('HeadlessChrome'),
viewport: `${window.innerWidth}x${window.innerHeight}`
};
log(`环境检测结果: ${JSON.stringify(envInfo, null, 2)}`);
const statusHtml = `
<h3>环境检测结果</h3>
<p><strong>Huggingface环境:</strong> <span class="status ${isHF ? 'success' : 'warning'}">${isHF ? '是' : '否'}</span></p>
<p><strong>主机名:</strong> ${hostname}</p>
<p><strong>浏览器:</strong> ${envInfo.isChromium ? 'Chromium/Chrome' : '其他'}</p>
<p><strong>Docker环境:</strong> ${envInfo.isDocker ? '是' : '否'}</p>
<p><strong>视口尺寸:</strong> ${envInfo.viewport}</p>
`;
updateResults(statusHtml);
// 更新环境状态显示
document.getElementById('environment-status').innerHTML = `
<strong>当前环境:</strong> ${isHF ? 'Huggingface Spaces' : '本地开发'}
<span class="status ${isHF ? 'success' : 'warning'}">${isHF ? 'HF环境' : '本地环境'}</span>
`;
}
// 模拟Huggingface优化的SVG转Base64
function huggingfaceOptimizedSvg2Base64(element) {
try {
log('开始Huggingface优化的SVG转换');
const clonedElement = element.cloneNode(true);
if (clonedElement.tagName.toLowerCase() === 'svg') {
const svgElement = clonedElement;
// 设置基本属性
svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
// 获取尺寸
const rect = element.getBoundingClientRect();
const width = rect.width || 100;
const height = rect.height || 100;
// 设置明确的尺寸
svgElement.setAttribute('width', width.toString());
svgElement.setAttribute('height', height.toString());
svgElement.setAttribute('viewBox', `0 0 ${width} ${height}`);
// 移除问题属性
const removeProblematicAttrs = (elem) => {
const problematicAttrs = ['vector-effect', 'xmlns:xlink'];
problematicAttrs.forEach(attr => {
if (elem.hasAttribute && elem.hasAttribute(attr)) {
elem.removeAttribute(attr);
}
});
if (elem.children) {
Array.from(elem.children).forEach(child => {
removeProblematicAttrs(child);
});
}
};
removeProblematicAttrs(svgElement);
// 序列化
let svgString = svgElement.outerHTML;
// 清理
svgString = svgString.replace(/vector-effect="[^"]*"/g, '');
svgString = svgString.replace(/xmlns:xlink="[^"]*"/g, '');
// 确保命名空间
if (!svgString.includes('xmlns="http://www.w3.org/2000/svg"')) {
svgString = svgString.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"');
}
log(`SVG字符串准备完成: 长度=${svgString.length}`);
// 编码
const base64 = btoa(unescape(encodeURIComponent(svgString)));
const result = `data:image/svg+xml;base64,${base64}`;
log('SVG转换成功');
return result;
}
throw new Error('不是SVG元素');
} catch (error) {
log(`SVG转换失败: ${error.message}`, 'error');
// 备选方案
const rect = element.getBoundingClientRect();
const width = rect.width || 100;
const height = rect.height || 100;
const placeholderSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
<rect width="100%" height="100%" fill="#f5f5f5" stroke="#ddd" stroke-width="1"/>
<text x="50%" y="50%" text-anchor="middle" dy="0.3em" font-family="Arial, sans-serif" font-size="14" fill="#999">SVG</text>
</svg>`;
const base64 = btoa(unescape(encodeURIComponent(placeholderSvg)));
log('使用占位符SVG');
return `data:image/svg+xml;base64,${base64}`;
}
}
// 测试SVG转换
function testSvgConversion() {
log('开始SVG转换测试');
const svgElements = document.querySelectorAll('.test-svg');
let successCount = 0;
let totalCount = svgElements.length;
const results = [];
svgElements.forEach((svg, index) => {
try {
const startTime = performance.now();
const base64Result = huggingfaceOptimizedSvg2Base64(svg);
const endTime = performance.now();
const duration = (endTime - startTime).toFixed(2);
if (base64Result && base64Result.length > 100) {
successCount++;
results.push({
id: svg.id,
status: 'success',
duration: duration,
size: base64Result.length,
preview: base64Result.substring(0, 100) + '...'
});
log(`SVG ${svg.id} 转换成功: ${duration}ms, 大小=${base64Result.length}`);
} else {
results.push({
id: svg.id,
status: 'error',
error: '转换结果无效'
});
log(`SVG ${svg.id} 转换失败: 结果无效`, 'error');
}
} catch (error) {
results.push({
id: svg.id,
status: 'error',
error: error.message
});
log(`SVG ${svg.id} 转换失败: ${error.message}`, 'error');
}
});
const successRate = ((successCount / totalCount) * 100).toFixed(1);
let resultHtml = `
<h3>SVG转换测试结果</h3>
<p><strong>成功率:</strong> ${successRate}% (${successCount}/${totalCount})</p>
<div style="margin-top: 15px;">
`;
results.forEach(result => {
const statusClass = result.status === 'success' ? 'success' : 'error';
resultHtml += `
<div style="margin: 10px 0; padding: 10px; border: 1px solid #ddd; border-radius: 5px;">
<strong>${result.id}:</strong>
<span class="status ${statusClass}">${result.status}</span>
${result.duration ? `<br>耗时: ${result.duration}ms` : ''}
${result.size ? `<br>大小: ${result.size} 字符` : ''}
${result.error ? `<br>错误: ${result.error}` : ''}
</div>
`;
});
resultHtml += '</div>';
updateResults(resultHtml);
log(`SVG转换测试完成: 成功率=${successRate}%`);
}
// 测试Huggingface渲染器
function testHuggingfaceRenderer() {
log('开始Huggingface渲染器测试');
const isHF = isHuggingfaceEnvironment();
// 模拟渲染配置
const config = {
useCORS: false,
allowTaint: true,
foreignObjectRendering: false,
scale: isHF ? 1 : 2,
logging: false,
timeout: 30000
};
log(`渲染配置: ${JSON.stringify(config, null, 2)}`);
// 测试每个SVG元素
const svgElements = document.querySelectorAll('.test-svg');
const testResults = [];
svgElements.forEach((svg, index) => {
try {
const rect = svg.getBoundingClientRect();
const hasValidDimensions = rect.width > 0 && rect.height > 0;
testResults.push({
id: svg.id,
dimensions: `${rect.width}x${rect.height}`,
hasValidDimensions,
childCount: svg.children.length,
status: hasValidDimensions ? 'ready' : 'warning'
});
log(`${svg.id}: 尺寸=${rect.width}x${rect.height}, 子元素=${svg.children.length}`);
} catch (error) {
testResults.push({
id: svg.id,
status: 'error',
error: error.message
});
log(`${svg.id}: 检查失败 - ${error.message}`, 'error');
}
});
let resultHtml = `
<h3>Huggingface渲染器测试</h3>
<p><strong>环境:</strong> ${isHF ? 'Huggingface Spaces' : '本地环境'}</p>
<p><strong>配置:</strong> CORS=${config.useCORS}, AllowTaint=${config.allowTaint}, Scale=${config.scale}</p>
<div style="margin-top: 15px;">
`;
testResults.forEach(result => {
const statusClass = result.status === 'ready' ? 'success' :
result.status === 'warning' ? 'warning' : 'error';
resultHtml += `
<div style="margin: 10px 0; padding: 10px; border: 1px solid #ddd; border-radius: 5px;">
<strong>${result.id}:</strong>
<span class="status ${statusClass}">${result.status}</span>
${result.dimensions ? `<br>尺寸: ${result.dimensions}` : ''}
${result.childCount !== undefined ? `<br>子元素: ${result.childCount}` : ''}
${result.error ? `<br>错误: ${result.error}` : ''}
</div>
`;
});
resultHtml += '</div>';
updateResults(resultHtml);
log('Huggingface渲染器测试完成');
}
// 测试导出功能
function testExportFunction() {
log('开始导出功能测试');
const testSvg = document.getElementById('svg1');
if (!testSvg) {
log('找不到测试SVG元素', 'error');
return;
}
try {
// 显示预览区域
document.getElementById('preview-container').style.display = 'flex';
// 显示原始SVG
const originalContainer = document.getElementById('original-svg');
const svgClone = testSvg.cloneNode(true);
originalContainer.innerHTML = '';
originalContainer.appendChild(svgClone);
// 转换SVG
const base64Result = huggingfaceOptimizedSvg2Base64(testSvg);
// 显示转换结果
const resultContainer = document.getElementById('converted-result');
const img = document.createElement('img');
img.src = base64Result;
img.className = 'preview-image';
img.onload = () => {
log('转换结果图片加载成功');
};
img.onerror = () => {
log('转换结果图片加载失败', 'error');
};
resultContainer.innerHTML = '';
resultContainer.appendChild(img);
const resultHtml = `
<h3>导出功能测试结果</h3>
<p><strong>状态:</strong> <span class="status success">成功</span></p>
<p><strong>Base64长度:</strong> ${base64Result.length} 字符</p>
<p><strong>数据类型:</strong> ${base64Result.substring(0, 30)}...</p>
<p>请查看下方预览区域对比原始SVG和转换结果。</p>
`;
updateResults(resultHtml);
log('导出功能测试完成');
} catch (error) {
const resultHtml = `
<h3>导出功能测试结果</h3>
<p><strong>状态:</strong> <span class="status error">失败</span></p>
<p><strong>错误:</strong> ${error.message}</p>
`;
updateResults(resultHtml);
log(`导出功能测试失败: ${error.message}`, 'error');
}
}
// 页面加载完成后自动检测环境
window.addEventListener('load', () => {
log('页面加载完成,开始初始化');
testEnvironmentDetection();
});
</script>
</body>
</html>