PLT(HPGL)文件是状态机式命令流,需手动建模坐标序列与笔状态(PU/PD),不可直接映射为GraphicsPath;须解析PA/PD/PU/AR等指令,维护当前坐标、落笔状态及路径段,弧线需离散化为多段线,单位默认密耳且需结合SC/IP推断坐标系。

直接解析 PLT(HPGL)文件中的矢量路径数据,不能靠 GDI+ 的 GraphicsPath::AddLines 或 GraphicsPath::AddArc 自动映射——HPGL 是状态机式命令流,没有内置路径对象,必须手动建模坐标序列和笔状态变化。
PLT 文件本质是命令驱动的状态机,不是图形对象容器
HPGL 不存储“路径”概念,只记录一连串带参数的两字符指令(如 PA、PD、PU、AR)。绘图仪靠维持当前坐标、当前笔号、当前抬/落状态来执行后续命令。这意味着:
-
PD后跟多个PA命令,构成一条折线段,但中间没有显式“开始新子路径”标记 -
PU→PA→PD才表示抬笔移动后落笔画线,漏掉任意一步就会连错路径 -
AR(弧)、CI(圆)、EW(楔形)等命令需按 HPGL 规范转换为多段线或贝塞尔近似,GDI+ 不提供原生支持 - 单位默认是密耳(mil = 0.001 inch),但文件可能含
IN(inch)、MM(millimeter)或未声明单位,需结合SC(scale)和IP(input P1/P2)推断实际坐标系
用 C++ 手动提取有效路径点序列的关键步骤
核心是构建一个状态结构体,逐行解析并累积点序列,同时识别路径起止边界。不推荐直接用 GraphicsPath 接收原始命令流——它无法反映 PU/PD 的语义切换。
- 定义状态结构:
struct HPGLState { PointF curPos; bool isPenDown; int penNum; std::vectorcurrentSegment; }; - 遇到
PU时:若isPenDown == true,把currentSegment保存为一条独立路径(push 到std::vector<:vector>>),清空currentSegment,设isPenDown = false - 遇到
PD时:设isPenDown = true;后续所有PA/PR坐标都追加进currentSegment(注意PR是相对坐标,要叠加curPos) - 遇到
PA或PR时:先更新curPos,再判断是否需要加入currentSegment(仅当isPenDown为真) - 忽略
SP、LT、LB等非几何命令,除非你需要还原图层或标注逻辑
HPGL 弧与圆命令(AR, CI)必须自行离散化
GraphicsPath::AddArc 接口与 HPGL 的 AR 参数不兼容:HPGL AR 格式是 AR x,y,r,sa,ea(中心、半径、起始角、终止角),而 GDI+ AddArc 要求矩形边界框和起始/扫过角度。更关键的是,HPGL 角度以“十分之一度”为单位,且 0° 指向正右,逆时针增加——和数学惯例一致,但需除以 10.0 转换。
- 不要尝试用
AddArc直接传参;应将AR拆成 8~16 段线段,用AddLines加入路径 -
CI x,y,r是整圆,等价于AR x,y,r,0,3600(3600 表示 360.0°),同样需离散化 - 若需高保真,可用三次贝塞尔拟合圆弧,但对大多数切割/绘图用途,20 段内接多边形已足够
- 注意:某些 HPGL 文件用
EA(绝对矩形边框)或ER(相对矩形边框)替代实心填充,这类命令不产生轮廓点,不应加入路径数据
内存与坐标精度陷阱:别让 strbuf 泄露毁掉整个解析器
你很可能复用开源 hp2xx 或 hpgl-dxf 的 C 解析逻辑,但它们普遍存在原始 C 风格内存管理缺陷。例如 hp2xx v3.4.4-2 的 hpgl.c 第 381 行:strbuf = malloc(strbufsize) 分配后从未 free,在长文件中会持续泄漏。
- 每次解析新命令前,确保
strbuf已free并重置为NULL;或改用std::string+std::getline替代手工fgets+malloc - HPGL 坐标常为整数(单位:mil),但转换到屏幕或 SVG 时需转
float或double;若用int存储中间坐标,超 2^31(约 2147 英寸)会溢出——实际 PLT 文件常达数万密耳,务必用 64 位整型或浮点暂存 - 不要假设所有命令都以分号
;结尾;有些老设备输出无分号,有些夹杂空格或制表符,strtok易误切,建议用sscanf配合格式字符串(如"PA%*d,%*d")跳过参数,再用strspn/strcspn定位下个命令
真正难的不是读出 PA 坐标,而是正确重建“哪些点属于同一条连续笔画”,以及处理嵌套抬笔/落笔、弧线方向、单位混用这些隐性上下文。一旦状态机逻辑写错,路径就会错连成蜘蛛网——这种问题在调试时几乎无法单步定位,只能靠打印每条生成的 currentSegment.size() 和首末点来交叉验证。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











