做芯片厂的内部业务系统,有一类需求看起来很简单,实际一碰全是坑:工程师遇到设备异常或者想更新一份工艺说明时,需要在TinyMCE这套富文本编辑器里插入一张CAD图纸,并且要求图纸不能糊、能放大看细节、打印出来线条还要清楚。我刚接手这类需求时,也以为“粘贴”一下就搞定了,结果发现从CAD软件里直接Ctrl+C再切到浏览器里Ctrl+V,得到的只是一张静态图,根本谈不上矢量输出。这篇文章就把我折腾下来的完整思路和实现过程整理一下,尤其是芯片制造企业里最关心的矢量保留问题。
1. 为什么芯片制造场景里,把CAD图纸贴进TinyMCE会成为一个真问题
1.1 先看典型业务:FAB内网文档系统里为什么会用到CAD图纸
很多人印象中芯片制造企业用的都是版图设计工具,跟AutoCAD这类CAD软件关系不大。但实际情况不是这样,芯片厂里跑的设备布局图、净化车间改造图、气体管路走向图、水电气配套图,甚至设备备件安装图,很多都是用CAD格式维护的。设备工程师在做保养记录、异常报告或者工程变更申请时,经常需要把相关图纸贴进说明文档里,告诉下一环节的人“问题出在哪根管路”“这次改造影响哪个区域”。
这类业务系统的前端,十有八九用的是现成的富文本编辑器,TinyMCE是比较常见的选型。工程师的理想操作流程是:在CAD软件里选中图纸内容,复制,回到网页编辑器里粘贴,再补充几句文字说明,保存提交。这个流程听起来顺畅,但问题恰恰出在“复制粘贴”这个动作上。
1.2 直接从CAD复制粘贴到TinyMCE会发生什么
从Windows系统剪贴板的底层行为说起。CAD软件复制对象时,通常会向剪贴板同时写入多种格式的数据,比如原生CAD对象、图元文件EMF、位图PNG/BMP、纯文本等。浏览器收到粘贴事件时,并不能直接识别CAD原生对象,只能读取它认识的那些标准格式。于是Chrome、Edge这些浏览器最终在编辑器里插入的,几乎都是来自剪贴板的PNG位图,或者一张被转换过的EMF渲染图。
位图意味着什么?意味着这张图的分辨率在复制那一刻就固定了。CAD图形里大量细线条、小尺寸标注、文字注释,只要一缩放,要么变模糊,要么出现锯齿。如果是A0或者A1的大图,转成PNG后文件体积还特别大,上传、保存、预览全都会被拖慢。更糟糕的是,如果文档后续要打印归档,位图的清晰度完全达不到工程档案要求。
还有个容易被忽略的问题:位图是无法选中和检索的。工程师想把图纸里的某个坐标、某个图号复制出来,或者想在文档系统里搜关键词,对着位图完全没法操作。这跟芯片制造企业重视可追溯性的习惯是冲突的。
1.3 我们要的“矢量输出”到底指的是什么
这个需求里的“矢量输出”,我理解下来其实包含三层意思。第一层是显示层的矢量,也就是图纸进入浏览器后,不是一张固定像素的位图,而是可以任意缩放不模糊的矢量图形,通常落地成SVG;第二层是编辑层的矢量,即图形元素在HTML文本中是真实存在的DOM节点或者可引用的资源对象,而不是一个死图片;第三层是业务层的矢量,图纸里的图形还能和图号、版本、责任人这些元数据挂勾,后续可以追踪来源。
三层都满足,才能算真正解决了问题。只做到第一层,比如想办法插入一张超高分辨率的长图,本质上还是没有解决矢量化。所以整个方案的核心,就是设计一条从CAD文件到浏览器端SVG的安全可控链路。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 实现方案选型:从DWG到浏览器可显示矢量的可行路线
2.1 主要路线横向对比
为解决“矢量输出”,我先后评估过几条路线。直接粘贴剪贴板位图,第一轮就被否掉,原因上面已经说了,它压根没有矢量信息。接下来说几条真正有可能保住矢量的路线:
第一种是前端解析DWG/DXF。思路是用浏览器里的JavaScript库直接把CAD文件解析成SVG,常见库有dxf-parser、three-dxf等。这套路对DXF格式还有一定可行性,因为DXF本质是文本格式,但遇到真正的DWG就比较吃力,尤其是高版本DWG,文件结构复杂且带压缩,纯前端解析很容易出现图元丢失、文字乱码的情况,开发量很大。
第二种是服务端转换,用专门工具把DWG/DXF转成SVG后回传前端。这条路线的转换质量稳定,能处理复杂图纸,而且代码只在服务端维护,前端只要上传和展示。对芯片厂内部系统来说,图纸文件本身就在内网流转,服务端处理也更安全可控。
第三种是所谓“伪矢量”方案,把CAD图纸先打印成高精度PDF,再在页面里嵌入PDF。PDF可以缩放,但它不是小型TinyMCE文档可以灵活使用的格式,编辑器的内容导出、字数统计、全文检索都很难处理,所以只能算旁路方案,不能作为主流程。
2.2 为什么选择“上传图纸+服务端转SVG”的路线
我最终定的是第二种,上传原始图纸文件到服务端,由服务端完成DWG到DXF再到SVG的转换,然后把SVG内容的访问地址返回给前端。这里有两个关键判断。
第一个判断是,不要把
