时间:2018年5月16日
DWG2000文件格式理解进阶版
(基于libopencad类库)
本文为dwg2000版本的浅析,阐述其结构和相关数据结构
AutoCAD在我国工程,建筑,测绘领域举足轻重,其图纸格式长为dwg和dxf格式。dxf格式相对于dwg稳定,但是内容上比dwg略有精简,而且读取时也比较长。Dwg的版面目前已经更新到R2018版本,但是其根源还是R2000版本,所以弄明白R2000版本对于后面研究dwg格式具有很大帮助。在研究dwg过程中,最大的问题是格式不公开,所以对于内部一些细节就不是很清楚,但是我们仍然可以了解一些大概,这对于AutoCAD的二次开发便很有帮助。
本文是通过解析libopencad类库(https://github.com/sandyre/libopencad)对dwg文件格式进行研究。
在研究方面,查阅了许多资料,其中云南省测绘工程院的汪燕麟在《测绘地理信息》上中提到由于我国地理测绘的1:1万,1:25万等国家基本比例尺地形图多以dwg和dxf保存,说明了研究的重要性。武汉工业学院的丰洪才将dwg文件当做图形数据库:
而西安科技学院的孙向红提出以下划分:
最后上海同济大学万明民在结合实际情况下提出:
本文参考了以上研究,又结合国外研究(https://www.opendesign.com/files/guestdownloads/OpenDesign_Specification_for_.dwg_files.pdf),在libopencad上面进行验证。
1. 文件的基本组成
R2000版本的DWG文件的基本组成如下所示:
●LOCATOR部分(索引部)-版本号和模块长度及起始地址,HEADER,CLASSES,TABLE,OBJECTMAP等部分的起始地址和大小
●HEADER部分(头部)-图的总体信息,DWG文件的头部变量
●CLASSES部分(类部)-包括应用程序定义的类的信息,这些实例将显示在 BLOCKS、ENTITIES 以及OBJECTS 部分。通常不包括用于充分用于与其它应用程序交互的信息。
●OBJECT MAP部分(对象映射部) – 句柄和位置的偏移:将其分别累加得到每个对象的句柄和起始地址
●OBJECT DATA部分(对象部) - 所有实体对象信息,表条目对象信息,词典条目对象信息等
对象部又具体地分为 表对象(表控制对象+表条目对象) //layer table –layer句
柄
//block table –block句柄
层对象(Layer)
块对象(BLOCK)
实体对象(ENTITIES)
抽象对象(OBJECTS)
●SECOND HEADER部分(应急头部)- 包含了恢复受损图形文件的重要信息。
●IMAGE DATA部分(图像数据)
2. 具体介绍:
首先是索引部分:
索引部主要包括文件版本号和文件维护版本号,图像数据部分的起始地址,以及各个模块的索引地址:如HEADER,CLASSES部分的起始地址。其中各个模块的索引+长度以记录(Record)的形式存放。
通过 ReadSectionLocators(),从0——17号单元为dwg文件的版本号,一般为“AC1015”,然后就是dwg具体内容部分,一般有6个索引位置,其中发现第一个(seeker[0])逻辑位置加上读出的位移偏移恰好等于第二个(seeker[1])逻辑地址,并将这些逻辑地址和偏移保存为seeker[i]。
接着就头部部分:
头部主要包含与该文件中所保存图形密切相关的系统变量的值
通过ReadHeader( eOptions )进入,利用从索引部分获得的seeker[0]进行文件流读取,开始16个字节为校验信息,第16——20号单元数据为头部的长度,接着就是一系列的读取数据并保存成对应格式,最后的18个字节单元,其中前2个字节单元为CRC校验码,最后16个字节为校验对比信息。
接着是类部部分:
类部包括应用程序定义的类的信息,这些实例将显示在 BLOCKS、ENTITIES 以及OBJECTS 部分。通常不包括用于充分用于与其它应用程序交互的信息。该部分为若干条类定义的集合【参考DXF文档中类的描述】。
通过ReadClasses( eOptions )进入,与读取头部类似,前16个字节为校验对比信息,接着第16——20号单元数据为类部的长度,接着也是读取文件流并保存成对应数据格式(在此部分是直接保存为CADClass类的实例的属性),最后第17——18号字节也是CRC校验码,最后16个字节为校验对比信息。
接着是对象映射部分:
对象映射部主要包含若干句柄和位置的偏移,将其分别累加后将得到每个对象的句柄和它的起始地址
通过CreateFileMap()进入,先读取其中第一部分Object map section 的长度(dSectionSize|个人觉得这里当做数据块个数更好),然后如果这个长度不等于2(也就是最小部分都为2)就进入循环,循环中的dSectionSize作为flag,当读取的字节数除8大于或等于dSectionSize就退出循环,并将最后坐标填入mapObjects,最后再读取CRC校验码,然后再读取下一个部分的长度,知道最后一个部分的dSectionSize等于2为止。对于此部分参考dxf文档应该是记录非实体的信息。
然后为对象部(广义上认为):
1)表对象:表是AutoCAD为方便索引而引入的数据结构。在DWG文件中所有的表均按一定顺序集中存放在表部。这些表包括块表(Block definition table)、层表(Layer table)、字型表(Textfont and shape table)、线型表(Linetype table)等等。每一种表的格式、长度均固定。如:对于层表,它包含了该层的全部信息:层名、该层实体的颜色、线型以及该层的状态(如是否可见、是否冻结)等,而每一种表的长度、个数和起始地址保存在头部的索引信息段。
2)块对象(Block):块是用AutoCAD进行绘图时经常遇到的一个概念。它是为了减少图形文件的长度和方便操作而引入的。AutoCAD将用户所做的块集中存在一起,这就是块实体部。每生成一个块时,AutoCAD同时自动地生成一个相应的块表,以便检索。对于每一个块,其两端均为块起始实体和结束实体,中间为该块所包含的各种实体元素。对块的寻址操作是通过块表中的信息和块实体部的起始地址来完成的。
3)实体对象(Entities):AutoCad绘图的基本图形对象
实体部保存着该图形中的全部实体。所谓实体(Objects)是指AutoCAD中的基本图形单元。如:点(Point),线(Line,Ray,Xline,Mline,Pline,Spline等)、圆(Circle)、弧(Arc)、块(Block)、尺寸标注(Dimsion)等等,均为AutoCAD的实体。为了识别实体,AutoCAD将实体进行编码,如点实体的编码为02H,直线实体的编码为01H等等。对于每一个实体,其数据结构都可以分成两个部分:实体头部和实体尾部。实体头部的长度固定,且其意义对每一个实体基本相同。它包含有实体类型编码、实体性质、实体长度等等。对于实体尾部,AutoCAD采用了极为紧凑的格式以节省占用资源。对于不同实体,其尾部数据结构不同,即使在同一类型的绘图命令中,根据复杂程度不同尾部数据的格式不同。因此,它的格式种类较多,在某些情况下实体尾部长度可以为零。例如:在直线实体尾部中,仅保存了直线两端点的坐标值;文字实体尾部中保存了文字的起始点坐标、高度、文字内容及旋转角度等等。在实体部中,各实体按一定次序存取。
4)抽象对象(Objects):非图形对象(如Dictionary
然后为应急头部:
为了防止由于索引部的重要索引信息的损坏而使整个DWG文件中的信息丢失,AutoCAD将一些重要索引信息的副本保存在DWG文件的尾部,成为应急头部。它包含了恢复受损图形文件的重要信息。
最后为图像数据部:
略
3. 数据类型:
DWG格式文件是二进制的,共有5种数据形式:字符型、字节型、整型(双 字节)、长整型(4字节)、双精度浮点数
CurrentViewportTable,
/*一套固定的200个AutoCAD定义的头变量
该段主要存放不具备对象特性的系统变量, 这些
变量记录了AutoCAD系统的当前工作环境, 包括
AutoCAD的版本号、插入基点、绘图界限的左上角
和右下角坐标、栅格间距、尺寸标注式样、当前图层
名、当前线型、当前颜色等。*/
BlocksTable,
/*块表(AcDbBlockTable类) 用于存储
AcDbBlockTableRecord类的对象, 记录了图形数据
库中块的定义, 其相应的AcDbBlockTable对象及
类操作主要与块有关。数据库中的实体通常隶属
于一个块表记录。图形数据库初始化后, 块表中已
经创建了MODEL - SPACE和PAPER - SPACE两
个记录。*/
LayersTable,
/*层表(AcDbLayerTable) 用于存储AcD -
bLayerTableRecord类的对象, 记录了图形数据库
中层的定义, 其相应的AcDbLayerTable对象及类
操作主要与层有关。图形数据库初始化后, 层表中
自动创建了一个系统默认的0层记录。*/
StyleTable,
/*尺寸标注样式表(AcDbDimStyleTable)
用于存储AcDbDimStyleTableRecord类的对象, 记
录了图形数据库尺寸标注风格的定义, 其相应的
AcDbDimStyleTable对象及类操作主要与尺寸标
注风格有关。*/
LineTypesTable,
/*线型表(AcDbLinetypeTable) 用于存储
AcDbLinetypeTableRecord类的对象, 记录了图形
数据库中线型的定义, 其相应的AcDbLinetype -
Table对象及类操作主要与线型有关。图形数据库
初始化后, 线型表中已经创建了CONTINUOUS、
BY - LAYER和BY - BLOCK三个线型记录。*/
ViewTable,
/*视图表(AcDbViewTable) 该表用来管理
当前图形数据库中所定义的视图, 其相应的AcDb -
ViewTable对象及类操作主要与视图操作有关, 在
DXF文件中, 该表为“VIEW”段。*/
UCSTable,
/*用户坐标表(AcDbUCSTable) 用于存储
AcDbUCSTableRecord类的对象, 用来管理当前图
形数据库中所用的用户自定义坐标, 其相应的
AcDbUCSTable对象及类操作主要与用户坐标系
有关。*/
ViewportTable,
/*视口表(AcDbViewportTable) 在Auto -
CAD中, 当系统变量TILEMODE设置为1时, 该
表用来管理用户所定义的视口, 在DXF文件中相
当于符号表中的“ VPORT”表, 其相应的AcDb -
ViewportTable对象操作与视口有关。*/
APPIDTable,
/*应用程序名注册表(AcDbRegAppTable)
用于存储AcDbRegAppTableRecord类的对象, 管
理图形数据库中已注册了的应用程序名, 所注册的
应用程序名主要用于扩展数据的管理, 其相应的
AcDbRegAppTable对象及类操作主要与应用程序
名的注册和扩展数据的管理有关。*/
EntityTable,
/*?????*/
ACADGroupDict,
ACADMLineStyleDict,
/*一个命名字典 它是AcDbDictionary类的命
名对象字典, 提供一个AutoCAD图的“目录”。图
形数据初始化后, 系统自动创建两个条目:GROUP
字典和MLINE字典。在MLINE字典中, STAN -
DARD为当前默认的线型。应用程序可以自由地
在这个字典中添加其它对象。*/
NamedObjectsDict,
LayoutsDict,
/*从AutoCAD用户界面的ActiveX中,布局的表示方式略有不同。
在ActiveX中,标准AutoCAD布局的内容被分成两个单独的对象
:layout对象和Block对象。布局对象包含布局设置和布局的视
觉属性,因为它出现在AutoCAD用户界面中。块对象包含布局的
几何图形。
每个布局对象都与一个块对象关联。要访问与给定布局相关联
的块对象,请使用块属性。相反,每个块对象与一个布局对象
关联。要访问与给定块关联的布局对象,请使用该块的布局属
性。在ActiveX中,除了纸张空间布局外,模型空间被认为是一
种布局。——参考Autodesk ActiveX and VBA Reference文档*/
PlotSettingsDict,
PlotStylesDict,
/*一个plot配置类似于布局; 因为两者都包含相同的情节信息。
区别在于,布局与包含几何图形的块对象相关联。plot配置与
特定的块对象无关。plot配置只是一个命名的plot设置集合,
可用于任何几何图形。*/
BlockRecordPaperSpace,
BlockRecordModelSpace
/*所以BlockRecordPaperSpace和BlockRecordModelSpace是属于BlocksTable的*/
5.表的理解:
1)AutoCAD图形是存储在数据库中对象的集合。符号表和字典是用来存储数据库对象的容器对象,这两种容器对象都可以将一个符号名映射到一个数据库对象。
2)对符号表进行操作时,必须打开对应符号表记录。
3)表记录添加到表中
4)一个实体都对应有零或多个表记录用于表达其样式。(或一个实体对应一个Dictionary,Dictionary对应零个或多个表记录)
6.AutoCAD中对象ID与句柄的区别:
对象的ID是在编辑过程中用来识别不同对象(概念上对象包含实体),而在最终存储至数据库中时,采用句柄来标示相应的对象。
7.获取对象ID的方法:
可以用迭代器遍历容器和对象列表获取对象ID。
8. Table和Dictionary区别:
符号表和命名字典是两种不同类型的容器对象,字典提供了一个存储对象更一般的容器,符号表与字典主要有以下两个区别。一是符号表中的记录不能被ARX应用程序直接删除,只能由AutoCAD的PURGE命令删除,或使用WBLock选择过滤器的块存盘操作删除,而一个字典所拥有的对象可以被ARX应用程序直接删除;二是符号表记录在其类定义中使用一个字段保存检索时用到的名称关键字,而字典则相反,检索时使用的名称与关键字独立于所关联的对象,它作为字典的一部分被保存起来
四.研究结论:
首先是存在索引头部的,其次头部长度在测试中不是固定的,对于table和Dictionary都是存放object条目(句柄)的容器。