做图纸自动化做了这么久我越来越确认一件事前几篇里讲直线、圆弧、块、图层都没那么难难的是最后出图时那些“要见人的东西”——注释和标注。你让程序画一条中心线很容易但要让程序像制图员一样把尺寸标得清楚、字不重叠、箭头不飞出图框那才是真正见功夫的地方。这篇是这个系列的第 6 篇我把 netDxf 处理注释和标注的完整思路整理出来单行文字、多行文字以及线性标注、对齐标注、角度标注、半径直径标注这些最常用的类型。示例代码都以 netDxf 较新的 3.x/4.x API 写法为准如果你还在用老版本个别方法名会有出入但思路完全可以照搬。1. 标注在 DXF 里的真实身份样式、测量和图形是分开存储的1.1 一个标注实体到底存了什么接触过 DXF 的人都会发现一个反直觉的事实你在 CAD 界面里看到的尺寸线、箭头、标注文字在 DXF 文件里并不是一组画好的线段加文字而是一个独立的“标注实体”。这个实体只保存关键的控制信息剩下的显示效果交给 CAD 软件按标注样式实时算出来。具体来说一个 netDxf 的标注实体Dimension的派生类内部大致包含定义点也就是被标注对象的特征点例如线性标注的两个端点、半径标注的圆心和圆周点、角度标注的顶点和两条边上的点。尺寸线偏移位置标注线放在距离被标注对象多远的地方。标注样式引用文字高度、箭头大小、文字与尺寸线的间距等等。测量值与文本覆盖原始测量值是程序根据几何计算出来的显示文字则在特殊情况下由“文本覆盖”字符串接管。类型枚举告诉 CAD 这是线性、对齐还是角度标注。这种存储方式的最大好处是标注可编辑。用户拿到你生成的图纸只要选中标注就能像普通 CAD 标注一样拖动尺寸线、改文字前缀、换样式如果你把标注拆成普通线条和文字去画那就失去了这个“原生属性”修改起来非常痛苦。1.2 为什么测量值要和显示文本分开刚开始用 netDxf 时我踩过一个很典型的坑我给一条 120mm 的直线做线性标注程序确实在线上方写了个“120”非常得意。后来发现如果我把样式里的文字精度改成两位小数CAD 里那行文字竟然没有跟着变。为什么因为在 DXF 标注模型里测量值是一个独立数据显示文本又是另一个数据两者默认是同步的但“文本覆盖”一旦存在CAD 就不会再自动更新显示文本了。netDxf 同样遵循这个逻辑。标注实体上有两个概念不要混淆Measurement根据定义点计算出来的真实长度/角度值只和几何有关。TextOverride用户强行指定的一串显示文字优先级别最高。换句话说如果你想显示的内容不是简单测量结果比如“2-Φ10”这种带数量的描述就必须写文本覆盖如果你只是想显示测量值本身那就什么都不用写甚至不应该写。1.3 netDxf 的标注类型对应关系netDxf 里常用的标注实体类有这么几种LinearDimension、AlignedDimension、AngularDimension、Angular2LineDimension、RadiusDimension、DiameterDimension少数版本还支持弧长标注。它们都从Dimension基类继承都可以直接挂到doc.Entities里。我建议你脑子里先把它们分成两拨一拨是“基于两点或边作距离测量”的线性类另一拨是“基于圆/圆弧作径向或角度测量”的径向类。这两拨的构造参数思路完全不一样后面写代码时分开记会轻松很多。2. 第一根线性标注最小可运行示例与三个关键参数2.1 准备项目并添加 netDxf如果你是从头开始一个控制台项目用 NuGet 搜索 netDxf 安装即可。需要注意netDxf 这个库最近几个版本把命名空间整理得很干净主要就是netDxf、netDxf.Entities、netDxf.Tables这几个日常使用不需要碰底层数据。using netDxf; using netDxf.Entities; using netDxf.Tables; DxfDocument doc new DxfDocument();要特别强调一点很多人在生成图纸时习惯先把几何实体全部画完再补标注这没问题但我建议你在代码结构上把标注逻辑独立成一个方法或类。标注参数多、样式杂混在几何代码里会很难维护。2.2 三点式的构造过程线性标注的本质是告诉程序“我要标哪两个点”再告诉它“尺寸线放在哪一侧”。下面是最小示例Vector2 p1 new Vector2(0, 0); // 被标注的第一个点 Vector2 p2 new Vector2(120, 0); // 被标注的第二个点 Vector2 offset new Vector2(0, -20); // 尺寸线相对位置 LinearDimension dim new LinearDimension(p1, p2, offset); doc.Entities.Add(dim); doc.Save(demo.dxf);这里新建一个LinearDimension传入的前两个点就是尺寸界线的起点和终点第三个参数是尺寸线的偏移向量。偏移向量的方向决定了标注出现在图形上方、下方还是侧边。上面例子传(0, -20)表示尺寸线在两点连线的下方 20 个绘图单位。这里最容易理解错的地方是第三个参数不是尺寸线的绝对坐标而是相对偏移。netDxf 会根据 p1、p2 的几何方向自动旋转尺寸线所以你给一个垂直于被测方向的偏移就可以不用自己计算标注线的终点坐标。比如标一条斜线偏移向量同样可以直接给法线方向上的值程序会正确处理旋转。如果你希望标注线不沿着两个定义点的方向而是固定一个旋转角度构造函数的其他重载也能做到。但 90% 的需求用最基础的“两点偏移”就够了别一开始就上复杂重载。2.3 默认样式与第一个显示效果上面这段代码保存出来的 DXFCAD 打开后会使用图纸默认的标注样式来渲染。也就是说文字高度、箭头大小全部是初值可能大得离谱也可能小得看不见。我建议从第一根标注开始就显式指定样式DimensionStyle style new DimensionStyle(MY-DIM) { TextHeight 2.5, ArrowSize 1.25, TextOffset 0.75, ExtensionLineExtend 2.5, ExtensionLineOffset 1.25 }; doc.DimensionStyles.Add(style); dim.Style style;这样生成的 DXF 至少在常规图纸比例下能正常显示后面第 5 章我会把样式参数逐个讲清楚。这里先记住一个原则标注样式在 netDxf 里是独立对象和线宽、颜色、图层一样需要提前准备好不要指望默认值能满足出图要求。3. 别把注释做成图块Text/MTEXT 的定位、对齐和换行细节3.1 单行还是多行看你的文字会不会断行DXF 里的注释文字主要有两种实体Text和MText。前者对应 CAD 里的单行文字适合零件编号、简短说明后者对应多行文字适合技术要求、标题栏里的大段描述。我的习惯是一旦文字内容里可能包含换行、制表符、不同字号就直接用MText。因为Text的换行处理非常别扭而MText天生就是为多行排版设计的。你如果拿Text去写一大段技术要求后面改行距、改对齐点的时候会后悔。3.2 一个标题栏注释的完整写法假设你要在产品图右下角写一个“技术要求表面不得有划伤”的注释块用MText的常用写法是这样的Vector2 pos new Vector2(180, 40); MText mtext new MText(pos, 3.0, 60, 技术要求\\P1. 表面不得有划伤\\P2. 未标注圆角均为 R1。) { AttachmentPoint MTextAttachmentPoint.TopLeft, Style textStyle }; doc.Entities.Add(mtext);注意MText的构造函数参数依次是插入点、文字高度、参考宽度和内容。\\P是 DXF 多行文字里的换行符在 C# 字符串中必须写成双反斜杠这个小细节我第一次写的时候被它坑到了——单反斜杠直接被当成非法的转义序列编译都不通过。那个参考宽度也很关键。MText的宽度决定了文字在哪一列开始换行它是“参考”宽度不是绝对限制。实际 CAD 里文字超出宽度时会自动换行但如果你给的宽度太小文字会挤成一团。我通常按最长一行能放下的宽度再乘 1.2 来设。3.3 对齐点文字基点和插入点不是一回事单行文字的定位比多行文字更反直觉。创建Text时传入的位置默认是文字的“左下角基点”但这个基点会随着对齐方式变化。比如你想把文字放在一个矩形的正中心传了矩形中心坐标结果运行出来文字跑到中心点的右上方就是因为没设置对齐方式。Text text new Text(new Vector2(100, 50), 2.5, A-A剖面) { Alignment TextAlignment.MiddleCenter, Rotation 0.0 };这里TextAlignment.MiddleCenter表示文字在水平和垂直方向都居中插入点坐标就是文字块的数学中心。净DXF 里这类对齐枚举对应的就是 DXF 的 72/73 组码和 CAD 端的行为是对应的。我在做批量图纸时踩过另一个坑单行文字设了对齐方式后某些 CAD 版本在炸开块或转格式时会把对齐信息丢失文字重新落到左下角整个图面就乱了。所以如果你这文件后面要给别人转来转去尽量少用单行文字的高阶对齐能用多行文字居中就用多行文字。3.4 特殊符号的转义直径、度数、正负号机械图纸里不可避免要写“Φ”“°”“±”这些符号。netDxf 里最稳妥的做法是直接写 Unicode 字符代码文件用 UTF-8 保存Φ10、45°、±0.05都能直接写进 DXF现代 CAD 都认识。如果你担心老版本 CAD 兼容性可以用 AutoCAD 的转义序列%%c是直径符号%%d是度数%%p是正负号。但这些转义序列只有在 CAD 解析时才会被解释netDxf 本身不负责转换。也就是说你在 MText 里写%%c10保存后打开 DXFCAD 会显示为 Φ10但如果用记事本看 DXF看到的还是%%c10。这个特性在做字符串比较时要小心不要拿显示效果去反推内部存的值。4. 常用标注类型的构造差异对齐、角度、半径与直径4.1 对齐标注不一定非要水平或垂直LinearDimension默认生成的标注线方向取决于两个定义点的连线方向但实际工程中经常需要“尺寸线永远水平”或者“永远垂直”的效果这时候会用到带旋转角的构造方式。而当你希望尺寸线“贴在被标注的斜边上”也就是 CAD 里的对齐标注时要用AlignedDimension。Vector2 p1 new Vector2(10, 10); Vector2 p2 new Vector2(80, 45); AlignedDimension dim new AlignedDimension(p1, p2, new Vector2(0, -12)); doc.Entities.Add(dim);对齐标注的显示结果是尺寸线和 p1、p2 的连线平行适合标注斜边的实际长度。它的测量值就是两点距离不像线性标注那样会把斜边投影到水平方向。做钣金件、斜面特征时这个类型很重要但因为它和线性标注长得很像我见过不少同事把这两个混用出来的图面上“45° 斜线标注的却是水平投影长度”这种低级错误。4.2 角度标注构造对象的思路完全不同角度标注在 netDxf 里分两种情况标一个圆弧的圆心角或标两条直线之间的夹角。圆弧角度标注基于Arc对象Arc arc new Arc(new Vector2(0, 0), 20.0, 0, 90); AngularDimension angular new AngularDimension(arc, 5.0); doc.Entities.Add(angular);第二个参数是角度尺寸线与圆弧外侧的偏移距离。如果你已经有一条圆弧实体直接传进去就行程序会取圆弧圆心和两条半径边作为角度定义点。两条直线之间的角度标注则用Angular2LineDimension它接收两条Line和一个标注位置。关键点是组成角度的两条直线可以是任意位置并不要求它们真实相交程序会按延长线交点计算角度。我在标注零件时常用这个特性比如标注两条平行加工面的夹角时实际图形里两条线并不相交但不影响标注。4.3 半径和直径标注旋转角是控制标注线方向的关键圆和圆弧的半径、直径标注在 netDxf 里非常直接Circle c new Circle(new Vector2(50, 50), 12.0); RadiusDimension rd new RadiusDimension(c, 0.0); DiameterDimension dd new DiameterDimension(c, 0.0); doc.Entities.Add(rd); doc.Entities.Add(dd);这里第二个参数是旋转角它决定了引出线往哪个方向延伸。0 表示水平向右。如果你希望标注线向左下或右上斜着出去给对应的角度值即可。实际出图时这个角度往往要根据图形布局反复调整因为标注文字默认放在引出线末端也就是说旋转角会直接影响文字的落点。我通常先给一个大概方向生成后拿到 CAD 里看一眼再回来改角度两三次就能调到理想位置。圆形对象和圆弧对象都可以传给半径、直径标注的构造函数。区别只在于一个标的是整圆一个标的是弧段程序会自动根据传入对象决定测量基准。4.4 常用标注类型对照表标注类型实体类核心构造输入典型用途容易踩的坑线性LinearDimension两点偏移向量水平/垂直距离偏移方向给错导致标注反向对齐AlignedDimension两点偏移向量斜边实际长度和线性标注混淆角度圆弧AngularDimension圆弧偏移圆心角偏移单位是绘图单位不是角度角度两线Angular2LineDimension两直线位置相交线夹角延长线交点方向不确定半径RadiusDimension圆/圆弧旋转角圆角半径旋转角是标注线方向不是文字角度直径DiameterDimension圆/圆弧旋转角孔径直径与半径标注用混这张表是我自己整理项目时的速查清单你可以直接抄去用。真正用的时候每个类型先各做一个最小样例存成 DXF 后用 CAD 打开核对一遍比背一百遍 API 都管用。5. DimensionStyle 参数清单从文本高度到公差控制5.1 一组能直接用的标称参数标注样式是 netDxf 出图效果最核心的部分。很多人以为标注生成不出来或位置不对是代码问题其实 80% 是样式参数没调对。下面这组参数是我在 1:1 毫米单位图纸上常用的基础配置DimensionStyle dimStyle new DimensionStyle(MY-ISO) { TextHeight 2.5, TextOffset 0.625, ArrowSize 1.25, DimensioningLineExtend 1.25, DimensioningLineGap 0.625, ExtensionLineExtend 2.5, ExtensionLineOffset 0.5, ExtensionLineFixedLength 0.0, TextVerticalAlignment TextVerticalAlignment.Above, TextInsideAlign true, TextOutsideAlign true }; doc.DimensionStyles.Add(dimStyle);不要小看TextOffset这个参数。它控制文字和尺寸线之间的空隙。空隙太大文字会离尺寸线很远看起来像两行字空隙太小或设为 0文字会直接压在尺寸线上。CAD 的国家标准规范里对这个值有推荐比例工程上一般取文字高度的四分之一左右上面我用的 0.625 就是 2.5 的四分之一。ExtensionLineOffset是尺寸界线从被标注点向外让出的距离。如果你做的是装配图这个值太小会导致尺寸界线和零件轮廓贴在一起视觉上非常乱适当地让出 0.5 到 1 个绘图单位会清爽很多。5.2 文字位置顺眼比正确更难标注文字的放置规则在DimensionStyle里有一组枚举控制TextVerticalAlignment表示文字在尺寸线上方、中间还是外部TextHorizontalAlignment控制水平方向。最常用的是TextVerticalAlignment.Above——文字在尺寸线正上方这也是国标最常见的形式。还有一对容易被忽略的参数是TextInsideAlign和TextOutsideAlign。它们控制文字在尺寸界线内部和外部时是否始终顺着尺寸线方向读。对于斜向标注文字默认会跟尺寸线平行但如果标注空间太小CAD 会把文字挪到尺寸线外部并转成水平方向。这两个布尔值就是控制这个行为的。我在标小圆角半径时经常遇到文字压线的问题把TextOutsideAlign改成 false 后文字被强制推出界外并且保持水平图纸立刻干净许多。5.3 文本覆盖表达式前缀后缀一起搞定现实中的标注很少只显示一个测量数字。最常见的需求是“Φ20”“R10”“4-M6”这类组合。netDxf 中这类效果主要靠TextOverrideLinearDimension dim new LinearDimension(p1, p2, offset); dim.TextOverride 4-M6;需要注意文本覆盖一旦设置CAD 就不再计算测量值。所以如果你只是想加个直径前缀而数字部分仍希望程序从几何算出正确的写法是把前缀和后缀写进覆盖字符串数字部分用占位dim.TextOverride %%c;这里的是 DXF 标注文本覆盖中的特殊占位符代表测量值。%%c表示在测量值前加直径符号比如测量值是 20显示就是 Φ20。这个占位符非常实用我项目里几乎所有直径标注都靠它实现动态显示而不是写死一个数字。5.4 公差没有直接 API 时的通用解法很多机械图纸要求标注公差比如“20±0.05”。netDxf 不同版本对公差的 API 支持程度不同如果你的版本没有直接给标标注实体设置公差的方法最通用、最稳妥的方案仍然是文本覆盖dim.TextOverride %%c20\\P%%p0.05;这里用\\P在标注文字里做了一个换行CAD 会显示成上下两行配合%%p正负号效果就是 Φ20 下一行写 ±0.05。这种做法在国标制图里是允许的而且程序逻辑非常简单不受 netDxf 版本限制。唯一要注意的是覆盖字符串里的数字已经完全取代测量值如果几何图元尺寸后来改了标注文字不会自动跟着变。这既是风险也是可控行为——想清楚你的使用场景就行。6. 读取和修改已有标注从别人图纸里“拆”出数据6.1 遍历所有标注实体读取 DXF 里的标注和读取其他实体一样遍历doc.Entities再按类型做模式匹配就能拿到所有标注对象。netDxf 的实体基类是EntityObject每种标注都从这里继承using (DxfDocument doc DxfDocument.Load(input.dxf)) { foreach (EntityObject entity in doc.Entities) { switch (entity) { case LinearDimension ld: // 处理线性标注 break; case AlignedDimension ad: // 处理对齐标注 break; case RadiusDimension rd: // 处理半径标注 break; } } }有一个细节我吃过亏CAD 图纸里的标注很可能被放在块定义内部而不是模型空间。如果你遍历doc.Entities发现没有标注先检查一下它们是不是在某个Block里。读取块内标注需要先遍历doc.Blocks再遍历每个块里的实体。6.2 修改已有标注保留样式引用是底线修改标注最常见三个动作改文字内容、改位置、改样式。它们的做法完全不同。改文字内容直接用TextOverride赋值这个最简单。但要分清你想改的是覆盖文本还是正常测量文本。如果原标注没有覆盖、展示的是测量值你一设置TextOverrideCAD 就会停止自动计算相当于把标注“冻结”成了一个自定义文本。我见过有人只是想把“20”改成“20±0.05”结果把整个标注变成了不跟随几何的文字后面几何一动数字就穿帮了。改位置时要特别注意标注位置涉及的属性不止一个。线性标注的位置由偏移向量决定你光改TextPosition可能只是把文字挪走了尺寸线还在原处。最保险的做法是重新构造标注实体替换掉旧的。因为在 netDxf 里标注的尺寸线位置是从构造参数推算出来的直接改属性容易出现“改了这里没改那里”的半吊子状态。改样式则相对安全给标注的Style属性重新赋值即可。前提是目标样式已经在当前文档的DimensionStyles集合里否则保存后部分 CAD 会报样式缺失。6.3 保存后的验证一定要做netDxf 读 DXF、改 DXF、写 DXF 的流程本身很稳定但标注这种复杂实体跨 CAD 软件兼容性差异大。我在批处理流程里加了一步“自动化验证”每次保存后用同一个库重新打开文件统计标注实体数量、检查有没有对象的Style为 null、确认文字覆盖没有异常字符。这步只要写几十行代码却能在交付给客户前拦截掉大量低级问题。另外一个非常实际的建议如果你要修改的文件是从别人的旧版本 CAD 导出的最好先让 netDxf 完整读写一次再对比前后文件。有的旧图纸里标注实体缺少样式引用或畸形定义点netDxf 加载时不会报错保存时也会原样写出但你的修改代码一旦触碰这些坏对象就会产生各种莫名其妙的异常。7. 实战出图踩坑记录字太小、数值错位、退化标注7.1 字太小、字太大比例因子的锅我最早一批自动生成的图纸交付出去后客户反馈“字看不清”。打开文件一看文字高度 2.5 是没错但整个图形是以 1:1 画在 A3 图框里的2.5 的字高在图面上当然小得可怜。CAD 的标注样式里有几个系统变量在控制文字和箭头的显示比例netDxf 的DimensionStyle参数用的是绘图单位不关心你最后出图缩放多少。所以你在设置TextHeight前必须先确认这个文件最终要在什么比例下打印。出 A3 图、1:2 比例那么实际字高至少要按打印字高乘以 2 来设。我后来做了一个更省事的方案把整个图形按比例统一缩放标注样式里的字高始终按最终出图尺寸设置。这样代码层只有一个ScaleFactor常量所有和出图比例相关的参数都乘以它避免每个标注单独换算。7.2 测量值和图形对不上投影方向的错觉有段时间我发现程序生成的线性标注测量值偶尔会“对不上”。后来定位到根本原因我给LinearDimension传入的两个定义点是斜线上的两个点但第三个偏移向量用的是垂直向下程序按默认投影方向计算了水平距离。这在几何上是正确的但看图的人以为标的是斜线长度自然觉得数值错了。解决办法有两种如果确实要标斜线的实际长度改用AlignedDimension如果就是要水平投影距离那两个定义点应该选在斜线端点的水平投影位置也就是说自己先做投影计算再喂给构造函数。别指望LinearDimension自己去理解你的“意图”它只认几何输入。7.3 退化标注两个定义点重合引发的崩溃netDxf 对退化几何的容错能力一般。我给一条长度只有 1e-7 的线段做线性标注时程序没有报错但生成的 DXF 在 CAD 里打开直接提示“存在无效标注已删除”。这种标注就是这个行业常说的退化标注——定义点重合、测量值为零或近零、圆弧标注对应弧段长度为零。我现在的代码里有一个强制检查尺寸线的两个定义点距离小于 1e-6 时直接跳过标注生成并记一条日志。别小看这个边界检查批量处理几千个零件时总会有个别零件尺寸为零你不处理它整个文件就会被 CAD 弹出警告非常掉价。7.4 跨软件显示不一致不要用轻量查看器当标准netDxf 保存的标注文件在不同软件里打开效果差异很大。CAD 完整版打开会重算尺寸线和文字位置显示效果好但一些轻量看图软件只做了很弱的标准注渲染可能把标注文字和尺寸线挤在一起。我的经验是程序生成的 DXF 必须以主流 CAD 软件的打开效果为准不要以代码输出结果为准更不要用轻量查看器截图判断布局。如果发现文字字体缺失优先检查你写的字体名是不是 CAD 内置的txt.shx、simplex.shx或常见 TrueType 字体。很多标注显示问题根本不是 netDxf 的问题而是字体名在目标系统里不存在。最后再分享一个我个人很受用的小技巧从项目一开始就建立一个独立的“标注验收图纸”里面包含你所有可能用到的标注类型、不同方向的偏移、不同长度的文本覆盖每次把 netDxf 升级版本后就用这张图纸跑一遍回归测试并保存。标注这种实体太依赖 CAD 端解析了光看代码根本预测不了显示效果有一张验收图纸版本升级后心里就有底。