C#实战:打造轻量级Gerber查看器,解析RS-274X与图形渲染
2026/9/9 21:46:31 网站建设 项目流程

简介:一款基于C#实现的Gerber查看器源码包,面向PCB设计、电子制造与CAM工艺人员,用于读取并可视化Gerber文件中的导线、焊盘、丝印等层信息,解决文件预览与后期检查需求。压缩包共35个文件,体积仅76KB,主体为11个.cs源文件,辅以项目配置(csproj/sln)、窗体与界面资源(resx/resources)、运行配置(config及exe)以及PDB调试符号等,可直接在Visual Studio中打开GMDO_V4.0项目进行编译与二次开发。目前已有3627人访问学习,适合具备基础C#知识、想深入理解Gerber解析流程或计划开发CAM Viewer的开发者。资源完整包含Form1窗体逻辑、文件列表管理、程序入口及属性设置,代码结构清晰,能直观演示如何按行解析指令、建立图形对象并通过WinForms/WPF渲染显示,为自研PCB查看工具提供了灵活可扩展的基础框架。

1. 为什么我要用 C# 写一个 Gerber 查看器

先交代背景。前几年做 PCB 相关项目时,频繁要在工厂、供应商、同事之间来回确认生产文件。Gerber 文件是 PCB 行业的通用“语言”,但问题在于:大多数桌面端 EDA 工具(AD、PADS、Allegro)体积大、启动慢、还吃授权,而 Gerber 查看器这块,要么是商业付费软件功能冗余,要么是网页版上传文件存在保密顾虑。于是自己动手用 C# 写一个轻量 Gerber 查看器的念头就冒出来了。

这个项目的定位不是替代 Cam350 这类专业工具,而是解决日常最核心的三个需求:

  • 快速打开 Gerber 文件确认图层内容、坐标、外形和拼板信息;
  • 不需要完整版 EDA 软件就能给结构、硬件、厂家快速截图并标注;
  • 离线和隐私安全,文件不出电脑,不用上传任何第三方服务器。

在技术选型上,我最终选定 C# 加 WinForms(后来也搬到 WPF 验证过),目标框架 .NET 6/8。原因很简单:C# 处理文本解析和图形绘制的效率足够高,GDI+ / System.Drawing 对 2D 位图和矢量绘制支持完善,而且 Windows 环境下部署方便,一个 exe 带个配置文件就能跑,连安装包都省了。

如果你是做硬件、嵌入式、PCB Layout 或者 PCBA 工厂端工艺的,这个项目可以直接套用;如果你是想入门 C# 文件解析和图形渲染的开发者,这个项目的难度曲线也很友好——核心不在算法,而在于把格式规则吃透、把边界情况处理好。

2. Gerber 格式到底在说什么:RS-274X 核心规则

先聊格式本身。Gerber 文件本质上是文本文件,每一行就是一条绘图指令,由不同的字母开头的命令字构成。最常用的是 RS-274X 标准,也叫 Extended Gerber。如果你打开一个 .gbr 文件,看到的可能是这样的内容:

%FSLAX26Y26*% %MOMM*% %ADD10C,0.200*% %ADD11R,1.500X0.250*% %LPD*% D10* X10000Y20000D02* X30000Y20000D01* M02*

这就是一个完整的小文件。它干了这些事:设置坐标格式为绝对坐标、6 位整数 6 位小数;单位是毫米;定义了第 10 号光圈为圆形直径 0.2mm,第 11 号光圈为矩形 1.5x0.25mm;然后移动笔、画一条线、结束。

解析 Gerber 有几个关键点必须吃透:

  • 坐标格式:FSLAX26Y26 表示 X 和 Y 都是 2 位整数加 6 位小数。实际坐标值需要根据这个精度做换算,比如 X10000 在当前格式下实际是 10mm。
  • 单位的坑:MOMM 是公制,MOIN 是英制。很多文件在毫米和英寸之间转换出问题,就是忘了读取单位指令。
  • 光圈定义:ADD 指令定义 D 码对应的形状,常见形状有圆形(C)、矩形(R)、方形(S)、热焊盘(O)和自定义轮廓。不同厂家的导出器支持范围不一样,有些非标准形状需要做兼容。
  • 绘图命令:D01 落笔画线,D02 抬笔移动,D03 曝光/画焊盘。这个三个指令几乎覆盖了所有图形绘制的基本动作。
  • 圆弧插补:G74 是单象限圆弧模式,G75 是多象限圆弧模式。圆弧不是直接给圆心坐标,而是通过终点相对起点的偏移量(IJ)和方向来确定,解析时必须算清楚象限和圆心位置。

对这些指令有一个整体认知之后,再看代码实现就不会迷路。很多人一开始就扎进代码写正则,结果遇到 G74/G75 圆弧就乱掉,本质上是格式规则没吃透。

3. 查看器的整体架构:解析、模型与渲染三层分离

代码层面,我把整个项目分成三层:解析层、数据模型层、渲染层。这样做的好处是后续可扩展性极强,比如如果你想增加导出位图、导出 DXF、甚至做拼板预览,只需要在对应层加方法就行。

数据模型层先定义几个核心类:

public class GerberFile { public FileFormat Format { get; set; } public List<Aperture> Apertures { get; set; } = new(); public List<GerberLayer> Layers { get; set; } = new(); public BoundingBox Bounds { get; set; } } public class Aperture { public int Code { get; set; } public string Shape { get; set; } public double Width { get; set; } public double Height { get; set; } public double HoleDiameter { get; set; } } public class ReportItem { public int X { get; set; } public int Y { get; set; } public bool IsExposure { get; set; } }

GerberFile 对应一个完整的图层集文件,Aperture 对应光圈定义,ReportItem 则是一个相对原始的“绘图动作记录”。渲染时把这些动作逐条读出来,即可在画布上绘制。

解析层的核心思路不是一次性把所有内容读进内存再处理,而是逐行流式解析。对于大文件(一份顶层铜箔 Gerber 可能有几十 MB),流式解析可以很好地控制内存占用。状态机是这个实现里最关键的部分,因为 Gerber 指令虽然以行为单位,但命令字可以连在一起写,比如上面的例子中每一行末尾是星号,而且某些指令会被连续输出。

状态机需要维护几个核心状态:当前光圈号(Current D-Code)、当前坐标(CurrentX / CurrentY)、笔状态(Pen Down / Up)、当前单位、当前坐标格式、当前圆弧模式。每次读取到新指令时,更新对应状态并输出绘图动作。遇到未知指令时先缓存起来并打印警告,而不是直接崩溃,这是兼容各种厂家导出文件的关键。

渲染层用 System.Drawing.Graphics,把模型层的数据按照视图窗口和缩放比例绘制到 PictureBox 或自绘控件上。这里有一个很实用的设计:先渲染到位图缓存,再显示到屏幕。因为 Gerber 文件可能包含几十万条绘图指令,如果直接在 Paint 事件里逐条画,拖动窗口和缩放时会卡到无法接受。缓存位图配合局部重绘,体验好很多。

架构确定之后,真正的开发难点在细节处理,下面逐块说。

4. 解析器与渲染过程的实操实现

4.1 用状态机吃透指令序列

我建议解析器不要用正则一家独大,而是用“读取指令 + 状态更新”的方式。核心循环大致这样:

while ((line = reader.ReadLine()) != null) { line = line.Trim(); if (string.IsNullOrEmpty(line)) continue; // 有些文件一行里会连续出现多个星号结束的指令 var parts = line.Split('*', StringSplitOptions.RemoveEmptyEntries); foreach (var part in parts) { ProcessCommand(part); } }

ProcessCommand 里根据首字母分流处理,例如遇到 FS 就更新坐标格式,遇到 MO 就更新单位,遇到 ADD 就新增光圈,遇到 D** 就切换当前光圈,遇到 X/Y 就更新坐标并触发绘图动作。这个结构很朴素,但非常稳健,因为 Gerber 指令是流式的,天然适合状态机。

坐标处理这里有一个必须提的细节:Gerber 中坐标字符串可能是 X00000Y00000D02 连写的,也可能指令和坐标混在一个字段里(比如 G75X123Y456D02)。所以解析时不能简单按固定位置截取,正确的做法是顺序扫描字符串中的每个字母(X、Y、I、J、D、G、M),用字母作为分隔边界提取数字部分。这也是我从实际项目里踩出来的坑——第一版用正则去匹配 XY 数字,遇到 X69 和 Y420 之间的边界时经常出错,后来改成扫描法之后,再没出过问题。

4.2 圆弧计算的细节

圆弧计算是 Gerber 解析里最容易被忽略的大坑。G74/G75 决定象限模式,IJ 给出的是圆心相对于起点的偏移量,而非绝对坐标。计算圆弧时,我先根据当前模式算出圆心坐标,再算出起始角和终止角,然后把圆弧细分成多段直线来逼近绘制。

// 单象限模式下,圆心相对于起点求偏移 double centerX = startX + iOffset; double centerY = startY + jOffset; double startAngle = Math.Atan2(startY - centerY, startX - centerX); double endAngle = Math.Atan2(endY - centerY, endX - centerX);

这里需要特别注意角度方向。Gerber 坐标体系里,默认是逆时针为正,但实际文件里经常会出现顺逆方向与预期相反的情况,所以最好把圆弧旋转方向也做成可配置项。另外,单象限模式时圆弧角度不能超过 90 度,如果起始角和终止角的差大于 90 度,往往是解析错误而不是源文件问题,需要回到坐标精度和 IJ 偏移的换算上排查。

4.3 渲染层的三件套:缩放、平移、量测

渲染层除了画图,还要支持几个常规操作:放大、缩小、适应窗口、十字光标读取坐标、测量两点距离。我用的方案是维护一个视图变换结构体:

public class ViewTransform { public double ScaleX { get; set; } = 1.0; public double ScaleY { get; set; } = 1.0; public double OffsetX { get; set; } = 0; public double OffsetY { get; set; } = 0; public PointF WorldToScreen(double x, double y) { return new PointF((float)(x * ScaleX + OffsetX), (float)(-y * ScaleY + OffsetY)); } }

画布坐标系把 Y 轴反转,这样 PCB 的物理方向和屏幕显示保持一致。缩放中心应该以鼠标当前位置为基准,而不是固定以左上角为锚点,否则放大时视口会漂移,这是很影响体验的细节。实现上其实就是先记录鼠标位置的物理坐标,然后更新变换参数,最后把该物理坐标映射回原屏幕位置,保证鼠标指向的点在缩放前后位置不变。

图层颜色管理也值得花点心思。顶层铜、底层铜、丝印、阻焊、钻孔层用不同颜色区分。我提供了一套预设配色:顶层铜红色、底层铜蓝色、丝印白色、阻焊绿色、钻孔黑色。这样光看颜色就能立刻判断是什么层。半透明叠加更是刚需,在检查顶层和底层是否对齐时,把两层叠在一起用透明色显示非常直观。System.Drawing 里 Color.FromArgb(alpha, r, g, b) 可以直接实现,性能上只要不是叠加太多层,完全能接受。

4.4 从 AD/PADS/Allegro 导出的文件该怎么适配

实际工作中,Gerber 文件来源五花八门。Altium Designer 默认导出的扩展名是 .GTL、.GBL、.GTO、.GBO 等,PADS 是 .pho,Allegro 是 .art。虽然格式标准都是 RS-274X,但各家在细节上埋了不少变量:

  • Altium 导出时通常会有 .GPI 文件记录光圈列表,不过标准 RS-274X 已经把光圈定义写进 Gerber 主文件里了,所以不需要额外解析外部光圈文件;
  • PADS 导出时默认把文件名截断为 8.3 格式,而且小数点分隔符可能与系统区域设置有关;
  • Allegro 导出时对精度设置非常敏感,经常有设计文件用了最高精度(比如 5 位整数 5 位小数),而自研查看器没适配到这么多位,就会导致坐标全部错位。

我的结论是:解析器对坐标格式的位数要尽量宽容,不要写死只支持 2:6 或者 2:5,最好做成动态读取 FSLA 指令里三位数字,并且在处理时对齐到同样的幂次。否则同一个文件在不同精度设置下导出的结果会天差地别。

5. 常见问题与排查技巧实录

项目做到中后期,我顺手整理了一个踩坑清单,这些坑在网上的博客里几乎找不到,但每一个都真实耗费过大量时间。

5.1 坐标整体偏移或者镜像翻转

如果你打开文件之后发现图形整体往一个方向飘了几毫米,十有八九是坐标格式解析不对。比如源文件是 2:5 格式(2 位整数 5 位小数),但你的解析器按 2:6 去读取,那么 X12345 就会被当成 0.12345 而不是 1.2345,整体缩放比不对。镜像翻转则要检查坐标是否应该做 X 或 Y 轴取反,这通常和导出时的镜像层设置有关,比如顶层视图和底层视图,从底层视角看自然就是镜像的。

5.2 圆弧变成直角或椭圆变形

圆弧变形最常见的两个原因:一是 IJ 偏移的精度丢失,二是在 G74 单象限模式下把跨象限的圆弧强行按单段直线绘制。前者要检查坐标格式解析是否正确,后者需要按象限拆分圆弧。还有一次遇到圆弧变成了椭圆,查了半天是 X 和 Y 方向的缩放比例不一致,最终发现作图控件宽高比没设置成 1:1,真凶反而在渲染层。

5.3 孔径(Aperture)形状匹配错乱

当文件里出现大量 ADD 指令,而且 D 码跨段使用时(不同段用同一个 D 码定义成不同形状),如果没有在解析时实时映射 D 码与形状,就会出现圆形焊盘画成矩形、矩形画成热焊盘的问题。解决办法是每次读取 ADD 指令时立即更新 DCode 映射表,而不是等到绘图指令时才去查旧映射。另一个细节是:某些导出器会把 D00 到 D09 保留给特殊用途,自定义形状往往从 D10 开始,这从 AD 和 Allegro 导出的文件里经常遇到。

5.4 大文件的性能优化

一份复杂的多层板 Gerber 全部打开后,绘图指令量以百万计,如果仍然逐条在 Paint 事件里调用 Graphics.DrawLine,那么每次重绘都可能卡顿。我用两级处理解决:先将渲染结果绘制到内存中的大位图,缩放或平移时先对位图做变换,等用户停止交互 300 毫秒后再重新渲染高精度缓存。再加上 .NET 提供的 BufferedGraphics,交互流畅度立刻提升一个档次。

5.5 单位显示混乱

有的文件同时使用英制线宽和公制板框,或者单位指令放置在不同位置。解析时不要只读取文件开头的一个 MO 指令,而要在每次需要用到单位的地方检查当前状态。视图层显示尺寸时,做成跟随当前文件单位切换显示,并提供一个下拉框在毫米/英寸之间切换,这样既能满足国内客户习惯,又能兼容国际厂家的需求。

6. 一些值得留存的实操经验和扩展方向

这个查看器在第一版可用之后,我陆续给组件做过几个轻量扩展,这里说两个比较有价值的。

第一个是 “PNG 快照导出”。给结构工程师看拼板信息、给供应商发生产问题时,一张带坐标基准尺寸的截图比一堆文件传起来直观得多。实现思路也不复杂:创建一个大位图,把当前所有可见图层按设定颜色渲染上去,再在边缘用 Graphics.DrawString 标上文件名称、缩放比例、单位、关键坐标点。这个功能在内部沟通中非常高频,基本成了每个版本都保留的固定功能。

第二个是 “极简 BOM 定位辅助”。Gerber 文件本身没有元件位号信息,但你可以通过解析钻孔层文件(Excellon 格式)或者读取坐标文件(比如 AD 导出的 Pick and Place 文件),把位号和 xy 坐标关联起来。之后点击位号就能在画布上定位到对应焊盘位置。这个功能算不上什么高科技,但配合 Board Fabrication 的审查流程非常方便。

在.NET 版本升级过程中,我还试过用 SkiaSharp 替代 System.Drawing 做渲染后端。坦白说,如果只是做基本的 Gerber 预览,System.Drawing 完全够用;但如果你有后续的跨平台计划(比如把查看器搬到 Linux 或者嵌入到 Web 服务里做自动化检查),那么从一开始就选定 SkiaSharp 或者 ImageSharp 作为渲染抽象会更省事。这个取舍没有标准答案,取决于项目边界和使用团队的习惯。

最后再分享一个我在实际项目中反复发现的现象:许多 Gerber 解析上的 bug,最终都不是标准不熟,而是“没有制作足够多样的测试文件”。我后来养成了一个小习惯——电脑里常年备着一套各 EDA 工具导出的测试套件,AD 的、PADS 的、Allegro 的、KiCad 的,每种工具出两个版本,一个简单线条板一个复杂混合层板。每改一次解析逻辑就全部回归跑一遍。就是这么笨的土办法,帮我挡掉了至少三次上线后才会暴露的回归 bug。如果你也想自己写一版自用 Gerber 查看器,我建议从备测试文件这件事开始做起,这个起点比先去翻任何源码都更踏实。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询