ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CAD图纸无缝嵌入TinyMCE:DWG转SVG全链路方案与踩坑实践

CAD图纸无缝嵌入TinyMCE:DWG转SVG全链路方案与踩坑实践 1. 先搞清楚CAD图粘贴到网页后到底丢了什么1.1 你粘贴进TinyMCE的只是“一张照片”半导体工厂里工艺工程师最怕的事不是设备宕机是辛辛苦苦画好的CAD图纸贴进MES、质量系统或BPM流程里的TinyMCE编辑器后变成一张“马赛克”。这不是个例。芯片制造企业的日常文档流里工程变更单ECN、不合格品处理单MRB、设备异常报告、工艺参数评审记录几乎每一条都要贴图纸。过去大家习惯了“CtrlC从CAD复制CtrlV到Word/Outlook”到了Web系统的富文本编辑器里这个习惯直接翻车。问题出在剪贴板协议上。当你从AutoCAD、中望CAD或Cadence这类专业工具里复制图元时Windows剪贴板通常会同时提供几种格式CF_BITMAP位图、CF_ENHMETAFILEWindows增强图元文件、有时还有CF_HTML或DWG特有的私有格式。浏览器在收到粘贴事件时绝大多数情况下只认位图这一路于是TinyMCE拿到的就是一个拍扁的PNG/BMP。我见过很多工程师现场崩溃一张版图或者一套洁净室设备布局图复制粘贴之后想放大看清某个焊盘、某条走线结果越放大越糊标注数字全是毛边。更麻烦的是位图一旦进入TinyMCE就彻底“死”了——没有图层、没有块、没有属性后续想在这个图上做标注、测距离、分层显示全部免谈。在很多开发者和办公用户眼里这不过是“图片清晰度”问题。但在芯片制造企业这个问题被放大了十倍。因为我们的图纸不是给人看的插图而是要拿来做审核、做追溯、做精度判断的依据。1.2 芯片制造场景对矢量图的硬性要求芯片行业的图纸和普通机械图有本质区别。以我经常经手的版图Mask Layout为例金属层、有源区、通孔阵列这些图元动辄几万到几十万个最小线宽和间距已经到了纳米级。即便只是设备布局图、封装基板设计图也涉及大量精细尺寸标注。这种图纸放到TinyMCE里业务上至少要满足三件事第一放大不失真。审核工程师需要把局部放大到能看清最小线宽、间距、轮廓细节位图做不到但矢量图可以无限缩放。第二图层属性要能追溯。哪个图形是Metal 1哪个是Via层哪个是标注文本CAD原始文件里区分得清清楚楚转化为网页内嵌内容后这些信息不能丢。第三与原始DWG版本可对应。质量体系审核时页面里看到的图和DWG源文件必须能对应上否则审计时说不清楚。所以表面上是“粘贴后清晰度不理想”的问题实际上是“如何把CAD的矢量信息无损搬进TinyMCE”的问题。这篇内容就是把我实际跑通的一套方案完整复盘工具链选型、转换链路、TinyMCE插件写法、还有那些不跑一遍绝对不会发现的坑。2. 四条技术路线我最后为什么选了SVG2.1 路线一把图纸转成SVG内嵌页面SVG是浏览器原生支持的矢量格式等于“网页里的CAD”。它的核心优势是无限缩放不糊、图层结构可以保留、文本可以被搜索而且DOM结构暴露在前端后续做交互点选图元、高亮、加批注都有天然基础。实现路径是把DWG先转成DXF再用Python的ezdxf库把DXF渲染成SVG最后通过TinyMCE插件把SVG内容嵌入编辑器。这条路线有什么代价CAD图纸本身极其复杂块定义Block、外部参照Xref、线型填充、SHX字体、代理对象任何一个处理不好都会在SVG里“降级”。所以需要转换前后做一轮清洗和验证不能只靠库一把梭。2.2 路线二用PDF承载浏览器直接渲染把CAD打印导出为PDF然后在TinyMCE里嵌入一个iframe指向PDF或用pdf.js渲染。这也是很常见的思路。优点是保真度极高。CAD的线宽、字体、填充、颜色映射全都由打印引擎处理过基本不丢东西。缺点是交互性几乎为零你不能在页面上单独选中某个图层不能做元素级批注而且PDF文件往往比SVG大不少。如果业务只是“审核归档”PDF这条线完全够用如果业务还要做在线批注、局部放大、版本对比PDF就显得笨重。2.3 路线三WebGL/Canvas自研解析渲染用开源解析器比如dxf-parser读DXF的几何图元然后自己用Canvas或WebGL画出来甚至可以做几十万图元的流畅缩放和平移。说实话这是能力最强的方案也是成本最可怕的方案。且不说DXF各种版本兼容问题光是线型管理、文字对齐、填充图案、坐标系转换、碰撞拾取就够一个团队干半年。除非你的场景是超大版图在线查看器否则芯片厂内部系统的投入产出比完全不成正比。2.4 路线四根子上不粘贴改走附件这是最容易被忽略的“兜底方案”。既然粘贴不可靠那就不要让用户粘贴——在TinyMCE里做一个自定义按钮用户点击后上传DWG文件本体页面里显示一个“图纸附件”卡片点击可下载原文件或调用在线看图服务。这个方案在涉及知识产权保护和版本管控的场景反而最实用。但产品经理们普遍不接受因为审核流程里需要在正文中直接看到图而不是下载后另开软件看。所以附件方案最终被我定位为“保底”SVG方案为主。2.5 选型结论按场景组合使用对芯片制造企业我的最终建议是三线并存工序内快速记录、审核说明用SVG内嵌方案最灵活可批注、可缩放正式签核、外发归档用PDF方案保真度最高不可篡改原件追踪附件方案兜底确保随时能拿回原始DWG。这个组合的核心理由是安全与可控。芯片厂普遍有内网隔离图纸基因里就带着保密属性第三方在线转换网站基本不用考虑数据绝对不能出内网。所以整套链路必须跑在企业内部环境里用自建服务完成。3. 完整实操DWG图纸如何一步步进入TinyMCE3.1 第零步把DWG统一转为DXFDWG是闭源私有格式而且AutoCAD每年都在更新内部结构直接解析DWG不现实。业内通用做法是先转成DXFDXF是公开的文本格式几乎所有开源库都能处理。转换工具我用的是ODA File Converter它从Version 6.0到R2018的DWG都能转而且是命令行工具可以批量跑。这对企业特别重要工程师丢过来一堆DWG文件总不能手动一个一个打开再另存。批量转换的脚本大体是这样# Windows环境下把某个目录下所有DWG转成DXF 2013格式 for /r D:\drawings %i in (*.dwg) do ( C:\Program Files\ODA\ODAFileConverter\ODAFileConverter.exe %i D:\dxf_out ACAD2013 DXF 0 1 最后一版.dxf )有人说可以用Python的ezdxf直接读取DWG这里必须澄清ezdxf.opens底层读的是DXF它对DWG的支持非常有限。所以转换这一步省不了老老实实过一遍ODA。参数里有几个坑要注意比如输出DXF版本。新版图纸如果业务上下游用的CAD版本较低建议统一转成ACAD2013兼容性最稳。另外输出文件名的处理ODA默认会保留原文件名如果你一次批量转同名不同目录的文件会出现覆盖建议在脚本里先做一次文件重命名映射。3.2 第一步用Python把DXF渲染成SVGDXF转SVG我用的方案是ezdxf的绘图后端。ezdxf维护得很好自带的drawing插件能处理绝大多数图元类型LINE、LWPOLYLINE、CIRCLE、ARC、SPLINE、HATCH、TEXT、MTEXT、INSERT都支持。先安装依赖pip install ezdxf matplotlib然后写一个最简转换脚本import ezdxf from ezdxf.addons.drawing import RenderContext, Frontend from ezdxf.addons.drawing import matplotlib as mpl_backend import matplotlib.pyplot as plt def dxf_to_svg(dxf_path, svg_path, figure_width20, figure_height12): doc ezdxf.readfile(dxf_path) msp doc.modelspace() fig plt.figure(figsize(figure_width, figure_height)) ax fig.add_axes([0, 0, 1, 1]) ctx RenderContext(doc) backend mpl_backend.MatplotlibBackend(ax) frontend Frontend(ctx, backend) frontend.draw_layout(msp) fig.savefig(svg_path, formatsvg, bbox_inchestight, pad_inches0) plt.close(fig) if __name__ __main__: dxf_to_svg(input.dxf, output.svg)这段代码在图形不复杂的场景下直接能用。但芯片厂图纸往往有大量块引用和外部参照转出来之后常见问题包括块里的文字全挤在一个角落、填充丢失、图元位置错乱。我后面第4节会专门说排查。转换输出后我强烈建议加一个验证环节重新读一遍生成的SVG检查它的svg viewBox是否符合原始DXF的边界范围。因为很多诡异问题最后都追溯到边界框不对。3.3 第二步写一个TinyMCE插件把SVG“正经地”插进编辑器很多人以为拿到SVG之后直接复制粘贴进TinyMCE就完事了。实际情况是TinyMCE对不安全HTML有过滤机制内联SVG的几乎所有标签都会被剥掉。你粘贴进去以后图形消失只留下一段空白。解决办法有两个。一个是初始化时给tinymce.init加上一个配置让编辑器“放行”SVG相关标签tinymce.init({ selector: #editor, plugins: ..., toolbar: ..., extended_valid_elements: svg[*],defs[*],g[*],path[*],line[*],rect[*],circle[*],ellipse[*],polygon[*],polyline[*],text[*],tspan[*],use[*], custom_elements: svg,defs,g,path,line,rect,circle,ellipse,polygon,polyline,text,tspan,use });第二个建议是写一个真正的插件。因为图纸不是随便插入的它需要有编号、有版本、有缩放控制、有删除清理逻辑。插件里注册一个按钮点击后打开文件选择框读取SVG文本注入编辑器。一个能落地的最小插件代码如下tinymce.PluginManager.add(cad_svg_embed, function(editor) { editor.ui.registry.addButton(cadSvgUpload, { text: 插入CAD图, onAction: function() { var input document.createElement(input); input.type file; input.accept .svg; input.onchange function(e) { var file e.target.files[0]; if (!file) return; var reader new FileReader(); reader.onload function(ev) { var svgContent ev.target.result; var designNo prompt(请输入图纸编号例如 CAD-DWG-METAL1-001, ); if (designNo null) return; editor.insertContent( div classcad-svg-wrapper contenteditablefalse>tinymce.init({ selector: #editor, plugins: ... cad_svg_embed, toolbar: ... cadSvgUpload, extended_valid_elements: svg[*],..., custom_elements: svg,... });这里有一个非常重要的细节外层div一定设contenteditablefalse否则编辑器用户在不经意间点进SVG内部会把某个path或者text删掉结果整个图纸“缺胳膊少腿”。我在第一版没注意这个问题结果审核工程师在页面上复制一段文字时把图纸的子元素带出来一段愣是找了一下午才发现。3.4 第三步单位、比例和viewBox的处理芯片行业图纸单位五花八门版图GDSII用纳米机械制图用毫米部分封装图用微米有些老图还混着英寸。转换SVG进Web页面时单位问题直接决定viewBox和显示比例的正确性。拿到DXF以后先读HEADER段的关键变量doc ezdxf.readfile(input.dxf) units doc.header.get($INSUNITS, 0) # 0: 无单位, 1: 英寸, 4: 毫米, 6: 米, 9: 微英寸, 10: 密耳, 11: 码, 12: 埃, 13: 纳米如果是毫米但是图纸要放到网页上显示我们一般把实际尺寸毫米直接作为viewBox的数值范围然后网页层用CSS控制显示大小。例如图纸宽2000mm、高1500mm可以设置svg viewBox0 0 2000 1500 preserveAspectRatioxMidYMid meet同时CSS上给.cad-svg-wrapper svg设置max-width: 100%; height: auto;这样既有原始坐标系又能随容器宽度缩放。比例问题我建议不要在转换阶段硬算把原始单位、比例尺都记录在SVG文件的metadata中前端展示时如果需要“实际测量距离”直接从metadata里取比例。这一点在第5章会再展开。4. 踩坑实录与排查速查表4.1 字体乱码SHX字形和TrueType必须分开处理这是最让我头疼的坑。DWG里很多标注用的是CAD特有的SHX形字体比如txt.shx、hztxt.shx这些字体本质上不是系统字体ezdxf渲染时找不到对应字形就在SVG里输出一个方块或者直接跳过。当时我们的设备布局图里一圈几十个设备位号全变成“□□□□”车间主任看了直接问是不是图纸坏了。解决思路是转换前先做一次字体替换。遍历模型空间里的TEXT、MTEXT和block里的文本对象如果发现dxf.style指向的字体是SHX就改成TrueType字体import ezdxf def replace_shx_font(doc, fallbacksimfang.ttf): 将所有使用SHX字体的文本样式替换为TrueType字体 for text_style in doc.styles: # 如果字体定义里包含.shx则改为TrueType if text_style.dxf.font and .shx in text_style.dxf.font.lower(): text_style.dxf.font fallback doc ezdxf.readfile(input.dxf) replace_shx_font(doc) doc.saveas(input_fixed.dxf)替换之后再用前面的脚本转SVG文字基本能正常输出。如果还要追求观感建议fallback字体统一用思源黑体或微软雅黑不要用simfang这种衬线感太强的工程图标注用黑体最稳。4.2 图层颜色、线宽全部走样DXF转SVG时颜色映射规则其实很容易乱。CAD图层颜色index是从0到255的Aci色而SVG里用的是RGB/HEX。ezdxf的RenderContext内部做了映射但实际效果和CAD屏幕显示经常不完全一致。最典型的坑是“图层打印样式CTB”。CAD图纸的显示颜色和打印颜色是两套体系很多图纸为了出图好看图层颜色用浅色打印样式里映射成黑色。直接转SVG取了显示颜色结果大量浅色图元在白底网页上几乎看不清。我的处理办法是转换前先做图层归一化。把要的主图层全部遍历一遍统一指定颜色为黑色或深灰彻底绕开CAD里“显示色vs打印色”的分歧for layer in doc.layers: layer.dxf.color 7 # 映射为白色/黑色前端CSS再控制线宽同理。CAD的线宽是打印属性不是几何属性SVG里默认所有线都1px。如果图纸里区分粗实线、细实线、虚线转换后视觉层次会丢。需要在绘图前端或SVG后处理里按图层或实体属性重新映射stroke-width。4.3 大图纸把浏览器拖垮怎么办芯片厂的版图数据量你是没法想象的。我处理过一张金属层版图DXF文件本身有200多MB直接转SVG生成了80MB的文本文件打开编辑器的一瞬间浏览器分分钟卡死。这种情况下第一条原则是不要让用户把大SVG直接塞进TinyMCE。重型图纸和正文文档本来就是两种场景。我的处理策略是“分级显示”页面里只嵌入一个轻量概览SVG自动删除重复短线、缩减圆弧分段数、不上传大图元概览图上提供“查看高清大图”按钮点击后用独立的看图页面加载全量SVG或WebGL渲染器。概览SVG的数据量控制在2MB以内正文编辑不会卡审核需要细节时再跳到高清视图。轻量化的一个简洁方法是删除多余的图层。芯片图纸里标注层、辅助线层在总览场景经常可以去掉只保留几何主层。ezdxf里按层名过滤图元很方便doc ezdxf.readfile(large.dxf) msp doc.modelspace() DROP_LAYERS {DIM, TEXT, CENTER, HIDDEN} for entity in msp: if entity.dxf.layer in DROP_LAYERS: msp.delete_entity(entity) doc.saveas(large_light.dxf)如果删完还是大那就只能上分块切割了。把图纸按区域切成若干子图缩放时按需加载对应块。这个方案开发量大一般只有在做在线版图浏览器时才值得投入。4.4 方向反了、坐标偏了坐标系那点事DXF采用的是世界坐标系WCSY轴朝上而SVG和网页的坐标系Y轴朝下。看似只需要做一个Y轴翻转但实际操作时会因为图的基点、极轴、模型空间原点位置不同出现各种“整体偏移”和“旋转”。ezdxf的绘图后端在MatplotlibBackend内部已经处理了坐标翻转但如果你的供应商或自己写的解析器没有处理就会出现整个图上下颠倒的惨案。手动处理时公式是这样x_svg x_dxf - min_x y_svg max_y - y_dxf即先求包围盒然后以左下角为原点重新定位。反过来如果发现左右翻转多半是x轴线性变换符号有误。有一次我们收到的图纸转换后“缺了一半”查下来才发现DXF里对象的坐标是相对某个用户坐标系UCS画出来的而ezdxf.readfile默认按WCS读取。这种图最稳妥的办法是在AutoCAD里先用UCS命令把它转回世界坐标系再保存DXF。程序层面处理UCS偏移很痛苦不值得硬啃。4.5 常见问题速查表现象根本原因解决办法粘贴后图片模糊浏览器取了剪贴板位图改用SVG/PDF方案禁止直接粘贴SVG插入后空白TinyMCE过滤了SVG标签配置extended_valid_elements和custom_elements文字全是方块SHX字体无对应系统字体转换前遍历字体样式SHX替换为TrueType图元颜色发白看不清显示色和打印色不一致转换前对图层做颜色归一化线宽全部统一CAD线宽是打印属性按图层映射stroke-width大图卡死浏览器SVG文件过大图层过滤、删重复图元、分级加载图纸上下颠倒Y轴方向不一致手动翻转或依赖已处理坐标系的库块引用位置错乱UCS或外部参照未解析先回CAD转WCS再导出DXF5. 从单点功能到企业级落地5.1 把转换逻辑封装成后端服务单靠工程师本地跑Python脚本只能解决一次性的问题。企业落地必须把转换能力做成服务。我在项目里用FastAPI封装了一个转换接口前端TinyMCE插件不再直接读取本地SVG文件而是上传DWG到后端后端调用ODA命令行转DXF再用ezdxf生成SVG同时返回图纸的元数据文件名、单位、包围盒、图层清单。后端的核心接口伪代码from fastapi import FastAPI, UploadFile import subprocess, os import ezdxf from ezdxf.addons.drawing import RenderContext, Frontend from ezdxf.addons.drawing import matplotlib as mpl_backend import matplotlib.pyplot as plt app FastAPI() app.post(/convert) async def convert_dwg(file: UploadFile): # 1. 保存上传的DWG dwg_path f/tmp/{file.filename} with open(dwg_path, wb) as f: f.write(await file.read()) # 2. ODA 转换 DWG - DXF dxf_path dwg_path.replace(.dwg, .dxf) subprocess.run([ ODAFileConverter, dwg_path, /tmp, ACAD2013, DXF, 0, 1 ], checkTrue) # 3. 渲染 SVG doc ezdxf.readfile(dxf_path) msp doc.modelspace() fig plt.figure(figsize(20, 12)) ax fig.add_axes([0, 0, 1, 1]) ctx RenderContext(doc) backend mpl_backend.MatplotlibBackend(ax) Frontend(ctx, backend).draw_layout(msp) svg_path dwg_path.replace(.dwg, .svg) fig.savefig(svg_path, formatsvg, bbox_inchestight) # 4. 返回SVG内容 元数据 units_code doc.header.get($INSUNITS, 0) extents doc.header.get($EXTMIN, (0, 0, 0)), doc.header.get($EXTMAX, (1, 1, 0)) return { svg: open(svg_path, encodingutf-8).read(), units: units_code, extents: extents }做了服务化之后TinyMCE插件就变成调用这个接口把返回值里的SVG插入正文。好处是图纸格式统一、转换规则可版本管理、日志可审计后端的权限控制和防病毒扫描也能一起接上。5.2 给图纸加一张“身份证”企业落地最容易被忽视的就是metadata。图纸进到TinyMCE里以后它不是一个孤立图片而是要参与审批流程的工程对象。所以转换后的SVG必须在metadata里带上这些字段字段示例值用途design_noCAD-DWG-METAL1-001唯一编号关联DWG源文件revisionA2设计版本防止新旧混淆unitsmm / um / nm单位换算依据scale1:100前端显示比例换算source_filemask_metal1_a2.dwg原始文件名designerZHANG_WEI责任设计audit_statusPENDING审核状态timestamp2024-06-18 14:30:22转换时间便于审计有了这些字段前端可以随时弹出“这个图对应的是哪个版本、谁画的、审核到哪一步”的面板。更重要的是当出现质量问题需要追溯时后端可以直接根据design_no revision检索到对应DWG源文件做到“页面所见”和“原始文件”一一对应。5.3 权限、审计和版本管理的建议芯片行业的图纸是核心资产在线化之后权限控制必须严谨。我建议至少做到三个层级普通用户只能查看、工艺工程师可以插入图纸和添加批注、设计主管可以覆盖更新版本。TinyMCE层面的编辑权限是一回事后端接口也要做同样的校验因为不能只靠前端按钮隐藏来防人操作。版本管理方面最简单的做法是转换服务每次生成SVG时自动用design_no revision 时间戳命名并把映射关系写进数据库。这样TinyMCE里即使插入了同一个图的不同版本也能在数据库里被区分开。审核流程中如果发现贴错了版本可以沿着映射关系直接追溯是哪个人在什么时间贴的。最后补充一点整套系统上线时最好先选一个部门试点不要一上来就铺开。我当时的经验是先用设备工程部当小白鼠因为他们图纸量适中问题反馈快一个月就能把转换规则打磨稳定再推广到全厂就顺畅很多。6. 后续还能怎么扩展这个方案跑稳定之后扩展空间其实很大。我目前已经在尝试的方向有两个。一个是把SVG再变成“可批注的图纸”。因为SVG的图元都在DOM里给每个path节点加>
返回列表