前阵子帮一家芯片制造企业的信息化部门处理了一个挺具体的需求:他们的工艺文档系统里,编辑人员需要把CAD图纸粘贴到TinyMCE富文本编辑器中,最终产出的文档不能是模糊的位图,必须保留矢量输出能力。这个需求听起来就是“复制粘贴”四个字,真的落地时才发现,牵扯到的技术点、文件格式、编辑器配置问题,比想象中多得多。
这个场景在芯片制造企业里其实非常典型。工艺规程、作业指导书、设备点检表、封装基板加工说明,哪一份文档都离不开图纸。过去大家的做法是把CAD导出成PNG或JPG再贴进去,图小看不清,图大占篇幅,放大一点全是马赛克,打印出来尺寸标注直接糊掉。更要命的是,一份光刻版图或晶圆夹具有几百个图层,位图根本承载不了这么多信息量。所以我今天就以这个真实需求为主线,把从CAD原始图纸到TinyMCE内嵌矢量SVG的完整方案、选型逻辑、实操步骤和踩坑记录都写清楚。
这篇文章适合谁看?如果你正在做企业级文档系统、PLM/PDM系统里的富文本编辑功能,或者你被分配了一个“把CAD图纸塞进网页编辑器还要求清晰”的奇葩需求,又或者你只是对SVG在企业系统里的工程化落地感兴趣,这篇文章都能给你一个可以直接抄作业的思路。
1. 先想清楚:这个需求的本质是什么
1.1 芯片制造场景下,图纸不是“一张图”那么简单
先别急着写代码,我们得先搞清楚使用方到底要什么。芯片制造企业里的CAD图纸,跟普通机械加工图纸有个很大的不同:它对图层、尺寸、公差的准确性要求极高,而且常常是多层结构。光刻掩膜版图、晶圆切片图、封装基板线路图,动不动就是几十个图层叠在一起。工程师看图纸的时候,可能要单独看某一层的走线,也可能要量两段之间的距离。
如果把这种图纸输出成位图,比如1920像素宽的PNG,放在电脑屏幕上还行,一旦打印成A3图纸或者局部放大去核对尺寸,精度就不够了。这就是企业非要“矢量输出”的根本原因。矢量图放大多少倍都清晰,线条、文字、填充区域全是数学描述,打印出来跟CAD里看到的几乎一致。
另外,这类图纸大部分属于企业内部敏感数据。整套系统只能部署在内网,不能依赖任何外部在线转换服务。这意味着所有转换工具、依赖库都必须能离线运行,这一点从一开始就决定了方案的走向。
1.2 TinyMCE处理SVG的底子到底怎么样
做过富文本编辑器二次开发的朋友应该都知道,TinyMCE默认对SVG的支持非常有限。它的内容模型是基于HTML schema的,默认情况下<svg>、<path>、<circle>这些标签根本不在允许列表里。你把一段SVG代码直接粘贴进编辑器,提交之后会发现SVG被过滤得只剩一个空壳,甚至整个标签都消失。
这不是TinyMCE有bug,而是它的设计如此:富文本编辑器要防范XSS攻击,滤镜、脚本、外部引用都会被当成风险内容处理。SVG又偏偏是个可以嵌入脚本、可以引用外部资源的格式。所以我们在方案里必须显式地告诉TinyMCE:哪些SVG标签是可信的、允许保留的,同时要在服务端存储时做必要的清洗。
1.3 “粘贴”拆开看:编辑态、存储态、输出态
我建议做这个需求时,不要只想着“怎么把图贴进去”,而要把内容流转分成三个阶段来设计。
编辑态就是用户在TinyMCE里看到的画面,SVG必须以可视化的形式渲染在编辑器区域内,最好还能拖动调整大小。存储态是表单提交后保存到数据库或对象存储里的内容,这一层要保证SVG完整、无污染、带版本信息。输出态是最终把文档变成PDF、打印件或者发布到只读系统时,SVG还能不能被正确渲染。
这三个状态一旦拆开,很多问题就变得清晰了。比如你可以允许编辑态在内容区直接内联SVG,存储态仍然内联保存,输出态交给浏览器或打印引擎去渲染。企业文档系统里,SVG完全可以作为一等公民对待,不需要退回成位图。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术路线对比:矢量输出不是只有一条路
2.1 路线A:DWG/DXF转PDF再转SVG
这是最稳妥的一条路,适合图纸已经管理得比较规范、有统一模板的企业。流程是:把CAD原图转成PDF,然后把PDF转成SVG。
为什么先转PDF?因为CAD软件自带的打印/导出功能一般都能直接出PDF,而且PDF对矢量信息保留得非常好。PDF转SVG的工具也很成熟,比如pdf2svg、pdftocairo,都是开源且可离线运行的。
这条路的优点在于实现成本低,几乎不需要写什么业务代码,CAD操作人员保持原有工作习惯。缺点是多了一次中间转换,如果原始DWG里有特殊字体、填充图案,转成PDF后再转SVG,可能出现文字移位、填充变形的小瑕疵,需要事后检查。
2.2 路线B:DXF直接解析生成SVG
这条路更“极客”一点,适合图纸数量特别大、需要批量自动化处理的场景。原理是绕过CAD软件,直接用程序解析DXF文件,把图纸里的直线、圆弧、圆、多段线、文字等实体提取出来,渲染成SVG标签。
编程语言我推荐Python,库用ezdxf。这个库能读取DXF里的模型空间、图纸空间,遍历所有实体,拿到每个实体的几何参数。理论上你可以完全自己控制输出,按图层分别导出、按颜色映射线宽,甚至能去掉标注层只留轮廓层。
它的优点是一旦管线跑通,后面就是纯程序处理,几千张图纸无非是时间问题,不用人工介入。缺点是需要做不少细节工作,比如坐标系转换、圆弧离散化、文字转路径、线型映射,工作量并不小。
2.3 路线C:开发TinyMCE插件,把SVG作为一等公民嵌入
这条路线其实不是替代方案,而是前面两条路线的必经出口。因为不管你是PDF转SVG还是DXF直接出SVG,最终都得有个办法把SVG“搬”进TinyMCE编辑区域。原生TinyMCE没有“插入SVG”按钮,所以我们要写一个简单的自定义插件,提供一个上传或粘贴SVG的交互入口。
插件做的事情很简单:弹出一个对话框,让用户选一个SVG文件,或者粘贴一段SVG源码,然后通过editor.insertContent()把SVG代码插入光标位置。同时配合extended_valid_elements配置,让TinyMCE不删除SVG相关标签。
这条路是整个方案的“最后一公里”,不做它,前面转换得再好也没用。但把它拆成独立模块来设计是有好处的,你可以随时替换图纸来源方式,插件层保持稳定。
2.4 如何选择:精度、速度、成本、维护性
三条路线怎么选,我建议从四个维度打分:精度、速度、成本、维护性。
路线A精度中等,实施速度最快,成本最低,维护性也不错,适合绝大多数中小规模企业。路线B精度最高,因为直接从DXF渲染几何实体,不存在二次转换失真,但开发成本高,适合图纸量特别大、有专职IT开发团队的企业。路线C是必选项,但不能单独存在,它必须配合A或B使用。
从我的经验来看,芯片制造企业如果有专门的CAD管理员,图纸模板统一,可以先上路线A保证业务能用,后续再逐步用路线B优化关键工序的图纸处理。不要一上来就追求全自动,先把链路打通更重要。
3. 实操:从DWG到TinyMCE的一条完整链路
3.1 图纸预处理:DXF统一、图层梳理、单位确认
不管走哪条路线,图纸预处理都是逃不掉的。最大的坑就是单位。CAD图纸里有的用毫米,有的用英寸,芯片行业甚至会用到微米。如果转换时单位不统一,出来的SVG比例全错,那这张图就没法用了。
我建议在企业内部规定:所有进入文档系统的图纸,统一用CAD软件“另存为”成DXF格式,并确认绘图单位。DXF文件本身带有单位信息,读取时要注意。用中望CAD或正版AutoCAD另存为DXF的时候,单位和图层一般能保留得很好。某些国产CAD软件另存DXF时可能会丢失填充图案,这个只能靠样本测试来排查。
图层梳理也很关键。芯片图纸里常有Center、Edge、Pad、Text、Dimension之类的命名约定。在转换前定好规则:哪些图层必须保留,哪些图层可以舍弃,文字层是否要转成轮廓。如果这一步不做,后面生成的SVG文件可能又大又乱。
3.2 PDF转SVG的批量做法
先说路线A的操作细节。最笨也最可靠的方法,是用CAD软件的批量打印功能,把一批图纸一次性输出成PDF。然后在一台内网服务器上装好poppler-utils,用pdftocairo批量转SVG。
pdftocairo是跨平台工具,Windows和Linux都有对应编译版本。命令行很简单:
bash复制pdftocairo -svg input.pdf output.svg
如果是批量处理,写个简单的shell脚本或者Python脚本调度就行:
bash复制for pdf in *.pdf; do
pdftocairo -svg "$pdf" "${pdf%.pdf}.svg"
done
不过直接把生成的SVG扔进TinyMCE前,我建议做一次检查,重点看两个地方:第一是SVG里的文字,如果PDF里嵌入了不常见的字体,转出来的SVG可能会引用系统中不存在的字体,需要确认转换成路径;第二是PDF尺寸,很多图纸是A0、A1大幅面,SVG的宽高数值会非常大,编辑器里显示时需要通过CSS限制最大宽度。
3.3 用Python直接解析DXF出SVG
如果走路线B,核心工作就是写一个DXF转SVG的转换器。ezdxf库是目前最成熟的方案,安装很简单:
bash复制pip install ezdxf
读取DXF并遍历实体,基础代码大概是这样的:
python复制import ezdxf
doc = ezdxf.readfile("chip_layout.dxf")
msp = doc.modelspace()
for entity in msp:
if entity.dxftype() == "LINE":
start = entity.dxf.start
end = entity.dxf.end
# 输出 <line> 标签
elif entity.dxftype() == "CIRCLE":
center = entity.dxf.center
radius = entity.dxf.radius
# 输出 <circle> 标签
elif entity.dxftype() == "TEXT":
text = entity.dxf.text
pos = entity.dxf.insert
# 输出 <text> 标签
要写出生产可用的转换器,还得处理LWPOLYLINE、ARC、SPLINE、HATCH这些常见实体。此外,CAD的坐标系是数学坐标,SVG的坐标原点在左上角,Y轴方向相反,所以导出时要做一个坐标系变换,把图纸的所有坐标翻转过来。
图层颜色映射也得注意。CAD里红色、黄色、青色各有含义,在输出SVG时可以按图层颜色自动映射成对应CSS颜色,这样转换出来的图纸跟CAD里看到的基本一致,工程师也更容易接受。
3.4 TinyMCE配置与自定义插件接入
现在到了最关键的“最后一公里”:让TinyMCE接受SVG。
首先是配置项。在TinyMCE初始化时,必须加上extended_valid_elements,把自己需要的SVG标签全部列入白名单。以TinyMCE 5/6为例,配置大概是:
javascript复制tinymce.init({
selector: "textarea",
plugins: "code",
toolbar: "undo redo | svginsert",
extended_valid_elements: "svg[*],defs[*],g[*],path[*],circle[*],rect[*],line[*],polyline[*],polygon[*],text[*],tspan[*],use[*],clipPath[*],mask[*],linearGradient[*],radialGradient[*],stop[*],view[*]"
});
如果不加这个配置,你调用insertContent()插入SVG,TinyMCE会把它当成未知HTML标签直接过滤掉,页面上一片空白。这个坑我踩得非常深,当时还以为是插件的问题,排查了半天才发现是schema白名单没加。
然后是自定义插件。我们可以在plugins目录下新建一个svginsert插件,注册一个工具栏按钮,点击后弹出文件选择框:
javascript复制tinymce.PluginManager.add("svginsert", function(editor) {
editor.ui.registry.addButton("svginsert", {
text: "插入SVG图纸",
onAction: function() {
let input = document.createElement("input");
input.type = "file";
input.accept = ".svg";
input.onchange = function() {
let file = input.files[0];
let reader = new FileReader();
reader.onload = function(e) {
let svgContent = e.target.result;
editor.insertContent(svgContent);
};
reader.readAsText(file);
};
input.click();
}
});
});
这段代码是最小可用版本,生产环境里还要做几件事:校验文件确实以<svg开头、限制文件大小、提示用户命名图号。如果你希望支持直接粘贴原始SVG代码,可以再增加一个菜单项,弹出一个textarea让用户粘贴内容。
还有一个经常被忽略的点:SVG插入后,编辑器里拖动尺寸可能不直观。建议在content_style里加上全局样式:
javascript复制content_style: "svg { max-width: 100%; height: auto; }"
这样既能防止大图纸撑破编辑器界面,也不影响打印时按原始尺寸输出。
3.5 提交、存储与打印输出的注意事项
SVG插入TinyMCE后,表单提交时它会被当成HTML字符串一起提交。这里要特别提醒:服务端保存时,一定不要随便做HTML转义或者去除标签操作,否则SVG会损坏。
还有,企业系统通常有安全扫描,如果WAF不认识image/svg+xml类型,可能会拦截包含SVG的请求。需要提前跟安全团队沟通,把SVG标签和MIME类型加入白名单。我遇到过一次非常尴尬的情况:本地测试一切正常,部署到测试环境后SVG总是保存失败,查了半天发现是网关把<path>标签当成攻击特征给拦了。
打印输出方面,浏览器直接打印TinyMCE内容时,SVG会按页面宽度缩放。如果你需要按图纸原始比例打印,建议在打印样式里用@media print单独设置SVG尺寸。或者更简单的方式:在保存出正式文档时调用window.print()前,临时把SVG的width属性改回原始图纸尺寸。
4. 企业落地时绕不开的工程化问题
4.1 内网环境依赖离线处理
芯片制造企业的内网环境通常不允许访问外网,所以所有工具都必须提前准备好离线安装包。poppler-utils、ezdxf、Inkscape都是可以离线部署的。
Inkscape在这里很有用,它可以用命令行把SVG进一步做优化:去掉无用的metadata、合并重复路径、转换文本到路径。比如这样:
bash复制inkscape input.svg --export-text-to-path --export-plain-svg -o output.svg
内网环境还意味着你不会遇到“pip install报错”这种问题,因为你可以用一台有外网权限的跳板机把依赖包全部下好,再拷贝进去。务必记录一套明确的依赖清单,否则下一个维护的人接手时,光找依赖就要折腾半天。
4.2 图纸台账:命名规范、版本管理与索引
图纸一旦进了文档系统,就不可能只是一张孤立的图片。它必须能被检索、被关联到对应产品型号、被追溯版本。我的建议是:不要让SVG文件裸奔,要给每张图纸建立一份台账。
命名规范可以参考企业现有的图号规则,比如产品代码-工序代码-版本号.svg。SVG文件本身放在一个独立的静态资源目录里,数据库表中保存图号、标题、版本、上传人、上传时间、关联的产品文档ID。这样即使TinyMCE里嵌入的是完整SVG字符串,后台依然可以通过图号索引到原始图纸文件。
版本管理方面,建议每次新图上传都生成新的图号版本,而不是覆盖旧文件。因为芯片制造行业对工艺文件追溯非常严格,旧版本必须能查得到。
4.3 权限与安全:水印、访问控制、审计日志
图纸安全是芯片企业的生命线。SVG是纯文本格式,任何人都可以用文本编辑器打开,把里面的路径数据复制走。所以权限控制一定要做在存储和访问层,而不能只靠编辑器。
我的做法是:文档系统里按项目、产品线划分目录权限,只有授权人员才能查看包含SVG的文档。SVG文件本身处于加密存储的目录中,前端通过带签名的临时URL加载,而不是直接暴露静态路径。这样即使有人拿到文档ID,没有权限也拉不到SVG源码。
有条件的企业还可以在SVG导出时自动打上数字水印,把用户工号和下载时间嵌入SVG的注释节点里。这样万一图纸泄露,能精准定位到责任人。
5. 常见问题与排查实录
5.1 粘贴后SVG被TinyMCE“吞掉”
这是最高频的问题,原因就是前面说的extended_valid_elements没配置。排查方法很简单:在TinyMCE里插入SVG后,切到源代码模式看<svg>还在不在。如果不在了,就是过滤规则的问题。
解决办法有两个层面。编辑层面,把需要的SVG标签全部加入白名单;服务端层面,确保保存时不走HTML富文本过滤接口。这两个地方都要改,缺一个都不行。
5.2 中文和特殊符号变成方块
DXF或PDF里的中文字体,如果转换过程中没有正确嵌入,渲染时就会变成方块或乱码。这通常出现在两条转换路径中。
PDF转SVG时,中文字体如果被简化成路径,一般不会乱码;但如果是保留文字形式,目标系统没有对应字体就会出问题。常用解决办法是转换时把文字统一转成路径,牺牲一点可编辑性换取显示稳定性。
DXF转SVG时,如果自己写转换器,TEXT实体的字体映射很容易忽略。建议干脆把中文字体都指定为系统安装的SimHei或Microsoft YaHei,或者把所有文字都调用Inkscape转成路径。
5.3 保存后图纸宽度撑破页面
这个现象很常见:SVG在编辑器里看是好的,保存后到另一个页面展示时,宽度变得巨大,把页面布局全挤坏了。
原因是TinyMCE里SVG受content_style约束,预览页没有这个样式。解决方法是给SVG统一加一个控制样式,比如限制最大宽度为100%,同时让它保持高度自适应。在内容和展示端都加上这段CSS,问题就消失了。
5.4 浏览器显示正常但导出PDF缺失
TinyMCE内容交给后端生成PDF时,如果用到的渲染引擎不支持SVG,就会出现PDF里图纸空白。常见的库如iText、Apache PDFBox对SVG支持都不好。
我的经验是:浏览器端打印优先,因为Chrome、Edge本身渲染SVG就非常可靠。如果是服务端生成PDF,可以把SVG临时转成PNG再放进去,但这就牺牲了矢量输出。更好的办法是选用支持SVG渲染的组件,比如用Chromium内核的headless浏览器去生成PDF,这样SVG依然是矢量。
5.5 大图卡顿与性能优化
芯片版图往往文件很大,一个SVG可能几MB甚至几十MB。TinyMCE打开包含大SVG的文档时,编辑区会明显卡顿。
对策有几个:一是编辑器里只放一个SVG预览入口,点击后才加载图纸,不把全量SVG都塞进内容区;二是用简化版SVG做编辑态预览,打印态才放完整路径;三是在SVG写入时做路径合并,减少节点数量。
我在实际项目里用脚本把所有连续的小直线段合并成<path>之后,一个2MB的SVG能瘦身到800KB,编辑器操作流畅度提升非常明显。
最后再说两句
这套方案做完之后,我又陆陆续续在别的企业项目里复用过几次,每一次都会根据图纸量、网络环境、团队技术水平做调整。我个人最大的感受是,CAD图纸进TinyMCE这件事,真正的难点从来不是TinyMCE这个编辑器本身,而是它的上游格式转换和下游存储安全。只要把图纸从DWG/DXF到SVG的转换链路做扎实,编辑器接入只是半小时的活。
一个小建议:如果你所在的企业也有类似需求,别急着让开发马上写转换脚本,先找五张典型图纸,人工走一遍PDF转SVG,再决定要不要上DXF直转。有些时候,最朴素的方案反而是维护成本最低的。真等到每天要处理上百张图纸的时候,再上自动化也不迟。
