You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#或JS中无第三方库程序化将SVG嵌入PDF文件

不依赖第三方库实现SVG转PDF(C#/JavaScript)

C# 实现思路与代码示例

要在C#里手动完成SVG到PDF的转换,核心是两步:解析SVG的XML结构提取图形元素,手动构建PDF文件的内容流(PDF本质是带结构的文本文件)。

核心步骤

  • 读取SVG文件,解析XML获取图形元素(如<path>、<rect>、<circle>等)。
  • 理解PDF的绘图模型:PDF使用左下角为原点的坐标系,以及m(移动)、l(直线)、c(贝塞尔曲线)等路径操作符。
  • 将SVG的图形指令转换为PDF对应的操作符,写入PDF的内容流。
  • 构建PDF的完整结构(文件头、目录页、页面对象、内容流、交叉引用表等)。

极简代码示例(仅处理矩形)

using System;
using System.IO;
using System.Xml;

class SvgToPdf
{
    static void Main(string[] args)
    {
        // 解析SVG获取矩形参数
        XmlDocument svgDoc = new XmlDocument();
        svgDoc.Load("input.svg");
        XmlNode rectNode = svgDoc.SelectSingleNode("//rect");
        float x = float.Parse(rectNode.Attributes["x"].Value);
        float y = float.Parse(rectNode.Attributes["y"].Value);
        float width = float.Parse(rectNode.Attributes["width"].Value);
        float height = float.Parse(rectNode.Attributes["height"].Value);
        string fill = rectNode.Attributes["fill"]?.Value ?? "#000000";

        // 构建PDF内容
        string pdfContent = $@"%PDF-1.4
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /MediaBox [0 0 612 792] /Contents 4 0 R >>
endobj
4 0 obj
<< /Length {GetContentStreamLength(x, y, width, height, fill)} >>
stream
q
{ConvertColorToPdf(fill)} {x} {792 - y - height} {width} {height} re f
Q
endstream
endobj
xref
0 5
0000000000 65535 f 
0000000010 00000 n 
0000000079 00000 n 
0000000146 00000 n 
0000000225 00000 n 
trailer
<< /Size 5 /Root 1 0 R >>
startxref
342
%%EOF";

        File.WriteAllText("output.pdf", pdfContent);
    }

    static int GetContentStreamLength(float x, float y, float w, float h, string color)
    {
        return $"q\n{ConvertColorToPdf(color)} {x} {792 - y - h} {w} {h} re f\nQ".Length;
    }

    static string ConvertColorToPdf(string hexColor)
    {
        if (hexColor.StartsWith("#")) hexColor = hexColor.Substring(1);
        float r = int.Parse(hexColor.Substring(0, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
        float g = int.Parse(hexColor.Substring(2, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
        float b = int.Parse(hexColor.Substring(4, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
        return $"{r} {g} {b} rg";
    }
}

说明:这个示例仅支持SVG矩形元素,若要支持路径、文本等复杂元素,需要解析对应的SVG指令并转换为PDF操作符,比如将SVG的path指令映射到PDF的m、l、c等操作。

JavaScript 实现思路与代码示例

JS场景分为浏览器环境和Node.js环境,核心逻辑是解析SVG并生成PDF结构,或借助内置Canvas中转。

浏览器环境(Canvas中转)

利用浏览器内置Canvas渲染SVG,再将Canvas内容转换为PDF图像数据:

async function svgToPdf(svgElement, pdfFileName) {
    // 把SVG序列化为DataURL
    const svgData = new XMLSerializer().serializeToString(svgElement);
    const img = new Image();
    img.src = `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svgData)))}`;
    
    await new Promise(resolve => img.onload = resolve);
    
    // 创建Canvas绘制SVG
    const canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);
    
    // 构建PDF基础结构
    const pdfHeader = `%PDF-1.4\n1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n3 0 obj\n<< /Type /Page /MediaBox [0 0 ${canvas.width} ${canvas.height}] /Contents 4 0 R /Resources << /XObject << /Im1 5 0 R >> >> >>\nendobj\n`;
    
    // 将Canvas内容转为PDF图像对象
    const pngData = canvas.toDataURL('image/png').split(',')[1];
    const pngBytes = atob(pngData);
    const contentStream = `4 0 obj\n<< /Length ${pngBytes.length + 20} >>\nstream\nq\n${canvas.width} 0 0 ${canvas.height} 0 0 cm\n/Im1 Do\nQ\nendstream\nendobj\n5 0 obj\n<< /Type /XObject /Subtype /Image /Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /FlateDecode >>\nstream\n${pngBytes}\nendstream\nendobj\n`;
    
    // 生成交叉引用表和尾注
    const xrefOffset = pdfHeader.length + contentStream.length;
    const xref = `xref\n0 6\n0000000000 65535 f \n${pad(pdfHeader.length, 10)} 00000 n \n${pad(pdfHeader.length + 65, 10)} 00000 n \n${pad(pdfHeader.length + 132, 10)} 00000 n \n${pad(pdfHeader.length + contentStream.indexOf('4 0 obj'), 10)} 00000 n \n${pad(pdfHeader.length + contentStream.indexOf('5 0 obj'), 10)} 00000 n \n`;
    const trailer = `trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xrefOffset}\n%%EOF`;
    
    const pdfContent = pdfHeader + contentStream + xref + trailer;
    
    // 下载生成的PDF
    const blob = new Blob([pdfContent], { type: 'application/pdf' });
    const link = document.createElement('a');
    link.href = URL.createObjectURL(blob);
    link.download = pdfFileName;
    link.click();
}

function pad(num, length) {
    return num.toString().padStart(length, '0');
}

// 使用示例:传入页面中的SVG元素
// svgToPdf(document.querySelector('svg'), 'output.pdf');

Node.js 环境(手动解析SVG+生成PDF)

用内置fs模块读写文件,手动解析简单SVG元素并生成PDF:

const fs = require('fs');

function svgToPdf(svgPath, pdfPath) {
    // 读取SVG文件内容
    const svgContent = fs.readFileSync(svgPath, 'utf8');
    // 简单提取矩形参数(复杂场景需完善解析逻辑)
    const rectMatch = svgContent.match(/<rect x="([\d.]+)" y="([\d.]+)" width="([\d.]+)" height="([\d.]+)" fill="([^"]+)"/);
    if (!rectMatch) {
        console.error('未找到可处理的矩形元素');
        return;
    }
    const [, x, y, width, height, fill] = rectMatch;
    
    // 构建PDF内容
    const pdfContent = `%PDF-1.4
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /MediaBox [0 0 612 792] /Contents 4 0 R >>
endobj
4 0 obj
<< /Length ${getStreamLength(x, y, width, height, fill)} >>
stream
q
${convertColor(fill)} ${x} ${792 - parseFloat(y) - parseFloat(height)} ${width} ${height} re f
Q
endstream
endobj
xref
0 5
0000000000 65535 f 
0000000010 00000 n 
0000000079 00000 n 
0000000146 00000 n 
0000000225 00000 n 
trailer
<< /Size 5 /Root 1 0 R >>
startxref
342
%%EOF`;
    
    fs.writeFileSync(pdfPath, pdfContent);
}

function getStreamLength(x, y, w, h, color) {
    return `q\n${convertColor(color)} ${x} ${792 - parseFloat(y) - parseFloat(h)} ${w} ${h} re f\nQ`.length;
}

function convertColor(hexColor) {
    if (hexColor.startsWith('#')) hexColor = hexColor.slice(1);
    const r = parseInt(hexColor.slice(0,2),16)/255;
    const g = parseInt(hexColor.slice(2,4),16)/255;
    const b = parseInt(hexColor.slice(4,6),16)/255;
    return `${r.toFixed(3)} ${g.toFixed(3)} ${b.toFixed(3)} rg`;
}

// 使用示例
// svgToPdf('input.svg', 'output.pdf');

注意:该示例仅支持简单矩形,若要处理复杂SVG,需自行实现完整的SVG DOM解析和指令转换逻辑。

内容的提问来源于stack exchange,提问作者KJohn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 13:30:47