第09章 - 格式转换实战
第09章 - 格式转换实战
9.1 格式转换概述
9.1.1 支持的格式
LibreDWG支持多种格式之间的转换:
| 源格式 | 目标格式 | 支持状态 |
|---|---|---|
| DWG | DXF (ASCII) | ✓ 完全支持 |
| DWG | DXF (Binary) | ✓ 完全支持 |
| DWG | JSON | ✓ 完全支持 |
| DWG | GeoJSON | ✓ 支持 |
| DWG | SVG | ⚠ 部分支持 |
| DWG | PS | ⚠ 部分支持 |
| DXF | DWG | ✓ 支持 |
| JSON | DWG | ✓ 支持 |
9.1.2 转换工具
dwgread # 通用读取器,支持多种输出格式
dwg2dxf # DWG到DXF专用转换器
dxf2dwg # DXF到DWG转换器
dwgwrite # 从DXF/JSON创建DWG
dwg2SVG # DWG到SVG转换器
dwg2ps # DWG到PostScript转换器
9.2 DWG与DXF转换
9.2.1 DWG转DXF
基本转换:
# 转换为ASCII DXF
dwg2dxf drawing.dwg
# 指定输出文件
dwg2dxf drawing.dwg -o output.dxf
# 转换为二进制DXF
dwg2dxf -b drawing.dwg -o output.dxf
# 最小化输出(去除冗余数据)
dwg2dxf -m drawing.dwg -o minimal.dxf
# 指定输出版本
dwg2dxf -r r14 drawing.dwg -o r14_output.dxf
批量转换脚本:
#!/bin/bash
# batch_dwg2dxf.sh - 批量DWG转DXF
INPUT_DIR="${1:-.}"
OUTPUT_DIR="${2:-./dxf_output}"
mkdir -p "$OUTPUT_DIR"
for dwg in "$INPUT_DIR"/*.dwg; do
if [ -f "$dwg" ]; then
filename=$(basename "$dwg" .dwg)
echo "转换: $dwg -> $OUTPUT_DIR/${filename}.dxf"
dwg2dxf "$dwg" -o "$OUTPUT_DIR/${filename}.dxf"
fi
done
echo "批量转换完成"
9.2.2 DXF转DWG
基本转换:
# 转换为DWG
dxf2dwg drawing.dxf
# 指定输出文件
dxf2dwg drawing.dxf -o output.dwg
# 指定DWG版本
dxf2dwg -r r2000 drawing.dxf -o r2000.dwg
支持二进制DXF:
# 二进制DXF也可以直接转换
dxf2dwg binary.dxf -o output.dwg
9.2.3 编程方式转换
#include <stdio.h>
#include <string.h>
#include <dwg.h>
// DWG转DXF
int convert_dwg_to_dxf(const char *dwg_file, const char *dxf_file)
{
Dwg_Data dwg;
int error;
// 读取DWG
memset(&dwg, 0, sizeof(Dwg_Data));
error = dwg_read_file(dwg_file, &dwg);
if (error >= DWG_ERR_CRITICAL) {
fprintf(stderr, "读取DWG失败: %d\n", error);
return -1;
}
// 写入DXF
error = dxf_write_file(dxf_file, &dwg);
dwg_free(&dwg);
if (error) {
fprintf(stderr, "写入DXF失败: %d\n", error);
return -1;
}
printf("转换成功: %s -> %s\n", dwg_file, dxf_file);
return 0;
}
// DXF转DWG
int convert_dxf_to_dwg(const char *dxf_file, const char *dwg_file)
{
Dwg_Data dwg;
int error;
// 读取DXF
memset(&dwg, 0, sizeof(Dwg_Data));
error = dxf_read_file(dxf_file, &dwg);
if (error >= DWG_ERR_CRITICAL) {
fprintf(stderr, "读取DXF失败: %d\n", error);
return -1;
}
// 设置输出版本
dwg.header.version = R_2000;
// 写入DWG
error = dwg_write_file(dwg_file, &dwg);
dwg_free(&dwg);
if (error) {
fprintf(stderr, "写入DWG失败: %d\n", error);
return -1;
}
printf("转换成功: %s -> %s\n", dxf_file, dwg_file);
return 0;
}
9.3 DWG与JSON转换
9.3.1 DWG转JSON
命令行转换:
# 完整JSON输出
dwgread -o json drawing.dwg > drawing.json
# 格式化JSON(配合jq)
dwgread -o json drawing.dwg | jq '.' > formatted.json
# 提取特定内容
dwgread -o json drawing.dwg | jq '.objects[] | select(.type == "LINE")' > lines.json
JSON结构示例:
{
"header": {
"version": "AC1015",
"codepage": 30
},
"header_vars": {
"ACADVER": "AC1015",
"INSUNITS": 4,
"EXTMIN": {"x": 0.0, "y": 0.0, "z": 0.0},
"EXTMAX": {"x": 100.0, "y": 100.0, "z": 0.0}
},
"classes": [],
"objects": [
{
"type": "LINE",
"handle": "1F",
"layer": "0",
"color": 256,
"start": {"x": 0.0, "y": 0.0, "z": 0.0},
"end": {"x": 100.0, "y": 0.0, "z": 0.0}
},
{
"type": "CIRCLE",
"handle": "20",
"layer": "0",
"center": {"x": 50.0, "y": 50.0, "z": 0.0},
"radius": 25.0
}
]
}
9.3.2 JSON转DWG
# 从JSON创建DWG
dwgwrite drawing.json -o drawing.dwg
# 指定版本
dwgwrite -r r2000 drawing.json -o drawing.dwg
9.3.3 JSON数据分析
# 统计实体类型
dwgread -o json drawing.dwg | jq '
.objects
| group_by(.type)
| map({type: .[0].type, count: length})
| sort_by(.count)
| reverse'
# 提取所有文本
dwgread -o json drawing.dwg | jq -r '
.objects[]
| select(.type == "TEXT" or .type == "MTEXT")
| if .type == "TEXT" then .text_value else .text end'
# 提取图层列表
dwgread -o json drawing.dwg | jq -r '
.objects[]
| select(.type == "LAYER")
| .name'
# 计算总线长
dwgread -o json drawing.dwg | jq '
[.objects[]
| select(.type == "LINE")
| pow(.end.x - .start.x; 2) + pow(.end.y - .start.y; 2)
| sqrt]
| add'
9.3.4 编程转换
#include <stdio.h>
#include <dwg.h>
// DWG转JSON
int convert_dwg_to_json(const char *dwg_file, const char *json_file)
{
Dwg_Data dwg;
int error;
memset(&dwg, 0, sizeof(Dwg_Data));
error = dwg_read_file(dwg_file, &dwg);
if (error >= DWG_ERR_CRITICAL) {
return -1;
}
// 写入JSON
error = dwg_write_json(json_file, &dwg);
dwg_free(&dwg);
return error ? -1 : 0;
}
// JSON转DWG
int convert_json_to_dwg(const char *json_file, const char *dwg_file)
{
Dwg_Data dwg;
int error;
memset(&dwg, 0, sizeof(Dwg_Data));
error = dwg_read_json(json_file, &dwg);
if (error >= DWG_ERR_CRITICAL) {
return -1;
}
dwg.header.version = R_2000;
error = dwg_write_file(dwg_file, &dwg);
dwg_free(&dwg);
return error ? -1 : 0;
}
9.4 DWG转GeoJSON
9.4.1 GeoJSON概述
GeoJSON是一种基于JSON的地理空间数据交换格式,适用于:
- GIS系统集成
- Web地图显示
- 空间数据分析
9.4.2 基本转换
# 转换为GeoJSON
dwgread -o geojson drawing.dwg > drawing.geojson
# 指定精度
# 编译时设置: ./configure --with-geojson-precision=6
GeoJSON输出示例:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"type": "LINE",
"layer": "0",
"color": 7
},
"geometry": {
"type": "LineString",
"coordinates": [
[0.0, 0.0],
[100.0, 0.0]
]
}
},
{
"type": "Feature",
"properties": {
"type": "CIRCLE",
"layer": "0",
"radius": 25.0
},
"geometry": {
"type": "Point",
"coordinates": [50.0, 50.0]
}
}
]
}
9.4.3 GeoJSON后处理
# 验证GeoJSON
npm install -g @mapbox/geojsonhint
geojsonhint drawing.geojson
# 使用ogr2ogr转换
ogr2ogr -f "ESRI Shapefile" output.shp drawing.geojson
# 简化几何
npm install -g geojson-simplify
geojson-simplify drawing.geojson > simplified.geojson
9.4.4 坐标系统处理
DWG文件通常使用本地坐标系,转换为GeoJSON时可能需要坐标转换:
#!/usr/bin/env python3
"""DWG转GeoJSON带坐标转换"""
import json
from pyproj import Transformer
def transform_geojson(input_file, output_file,
from_crs="EPSG:2436", # 本地坐标系
to_crs="EPSG:4326"): # WGS84
transformer = Transformer.from_crs(from_crs, to_crs, always_xy=True)
with open(input_file, 'r') as f:
geojson = json.load(f)
for feature in geojson.get('features', []):
geom = feature.get('geometry', {})
coords = geom.get('coordinates', [])
if geom['type'] == 'Point':
x, y = coords
coords[:] = list(transformer.transform(x, y))
elif geom['type'] == 'LineString':
for i, (x, y) in enumerate(coords):
coords[i] = list(transformer.transform(x, y))
elif geom['type'] == 'Polygon':
for ring in coords:
for i, (x, y) in enumerate(ring):
ring[i] = list(transformer.transform(x, y))
with open(output_file, 'w') as f:
json.dump(geojson, f, indent=2)
if __name__ == '__main__':
import sys
if len(sys.argv) >= 3:
transform_geojson(sys.argv[1], sys.argv[2])
9.5 DWG转SVG
9.5.1 基本转换
# 转换为SVG
dwg2SVG drawing.dwg > drawing.svg
# 指定输出文件
dwg2SVG drawing.dwg -o drawing.svg
9.5.2 SVG后处理
# 使用Inkscape打开和编辑
inkscape drawing.svg
# 使用svgo优化
npm install -g svgo
svgo drawing.svg -o optimized.svg
# 转换为PNG(使用Inkscape)
inkscape --export-type=png --export-filename=output.png drawing.svg
# 转换为PDF
inkscape --export-type=pdf --export-filename=output.pdf drawing.svg
9.5.3 自定义SVG生成
#include <stdio.h>
#include <dwg.h>
#include <math.h>
// 简单的SVG生成器
void write_svg_header(FILE *fp, double width, double height)
{
fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
fprintf(fp, "<svg xmlns=\"http://www.w3.org/2000/svg\" ");
fprintf(fp, "width=\"%.2f\" height=\"%.2f\" ", width, height);
fprintf(fp, "viewBox=\"0 0 %.2f %.2f\">\n", width, height);
fprintf(fp, "<g transform=\"scale(1,-1) translate(0,-%.2f)\">\n", height);
}
void write_svg_footer(FILE *fp)
{
fprintf(fp, "</g>\n");
fprintf(fp, "</svg>\n");
}
void write_svg_line(FILE *fp, double x1, double y1, double x2, double y2,
const char *color, double width)
{
fprintf(fp, "<line x1=\"%.4f\" y1=\"%.4f\" x2=\"%.4f\" y2=\"%.4f\" ",
x1, y1, x2, y2);
fprintf(fp, "stroke=\"%s\" stroke-width=\"%.2f\" />\n", color, width);
}
void write_svg_circle(FILE *fp, double cx, double cy, double r,
const char *stroke, const char *fill, double width)
{
fprintf(fp, "<circle cx=\"%.4f\" cy=\"%.4f\" r=\"%.4f\" ", cx, cy, r);
fprintf(fp, "stroke=\"%s\" fill=\"%s\" stroke-width=\"%.2f\" />\n",
stroke, fill, width);
}
void write_svg_text(FILE *fp, double x, double y, const char *text,
double size, const char *color)
{
fprintf(fp, "<text x=\"%.4f\" y=\"%.4f\" ", x, y);
fprintf(fp, "font-size=\"%.2f\" fill=\"%s\" ", size, color);
fprintf(fp, "transform=\"scale(1,-1)\">%s</text>\n", text);
}
// 转换DWG到SVG
int dwg_to_svg(const char *dwg_file, const char *svg_file)
{
Dwg_Data dwg;
int error;
memset(&dwg, 0, sizeof(Dwg_Data));
error = dwg_read_file(dwg_file, &dwg);
if (error >= DWG_ERR_CRITICAL) {
return -1;
}
// 计算范围
double min_x = dwg.header_vars.EXTMIN.x;
double min_y = dwg.header_vars.EXTMIN.y;
double max_x = dwg.header_vars.EXTMAX.x;
double max_y = dwg.header_vars.EXTMAX.y;
double width = max_x - min_x;
double height = max_y - min_y;
// 添加边距
double margin = width * 0.05;
width += 2 * margin;
height += 2 * margin;
FILE *fp = fopen(svg_file, "w");
if (!fp) {
dwg_free(&dwg);
return -1;
}
write_svg_header(fp, width, height);
// 遍历实体
for (BITCODE_BL i = 0; i < dwg.num_objects; i++) {
Dwg_Object *obj = &dwg.object[i];
if (obj->supertype != DWG_SUPERTYPE_ENTITY) continue;
switch (obj->fixedtype) {
case DWG_TYPE_LINE: {
Dwg_Entity_LINE *line = obj->tio.entity->tio.LINE;
write_svg_line(fp,
line->start.x - min_x + margin,
line->start.y - min_y + margin,
line->end.x - min_x + margin,
line->end.y - min_y + margin,
"black", 0.5);
break;
}
case DWG_TYPE_CIRCLE: {
Dwg_Entity_CIRCLE *circle = obj->tio.entity->tio.CIRCLE;
write_svg_circle(fp,
circle->center.x - min_x + margin,
circle->center.y - min_y + margin,
circle->radius,
"black", "none", 0.5);
break;
}
// 其他类型...
}
}
write_svg_footer(fp);
fclose(fp);
dwg_free(&dwg);
return 0;
}
9.6 批量转换工具
9.6.1 通用批量转换脚本
#!/bin/bash
# batch_convert.sh - 通用批量转换脚本
usage() {
echo "用法: $0 <输入目录> <输出格式> [输出目录]"
echo "支持的输出格式: dxf, json, geojson, svg"
echo ""
echo "示例:"
echo " $0 ./dwg_files dxf ./dxf_output"
echo " $0 ./dwg_files json"
}
if [ $# -lt 2 ]; then
usage
exit 1
fi
INPUT_DIR="$1"
OUTPUT_FORMAT="$2"
OUTPUT_DIR="${3:-${INPUT_DIR}/${OUTPUT_FORMAT}_output}"
mkdir -p "$OUTPUT_DIR"
# 统计
total=0
success=0
failed=0
for dwg in "$INPUT_DIR"/*.dwg; do
if [ ! -f "$dwg" ]; then
continue
fi
total=$((total + 1))
filename=$(basename "$dwg" .dwg)
case "$OUTPUT_FORMAT" in
dxf)
output="$OUTPUT_DIR/${filename}.dxf"
dwg2dxf "$dwg" -o "$output" 2>/dev/null
;;
json)
output="$OUTPUT_DIR/${filename}.json"
dwgread -o json "$dwg" > "$output" 2>/dev/null
;;
geojson)
output="$OUTPUT_DIR/${filename}.geojson"
dwgread -o geojson "$dwg" > "$output" 2>/dev/null
;;
svg)
output="$OUTPUT_DIR/${filename}.svg"
dwg2SVG "$dwg" -o "$output" 2>/dev/null
;;
*)
echo "不支持的格式: $OUTPUT_FORMAT"
exit 1
;;
esac
if [ $? -eq 0 ] && [ -f "$output" ] && [ -s "$output" ]; then
echo "✓ $filename"
success=$((success + 1))
else
echo "✗ $filename"
failed=$((failed + 1))
fi
done
echo ""
echo "转换完成"
echo " 总数: $total"
echo " 成功: $success"
echo " 失败: $failed"
9.6.2 Python批量转换
#!/usr/bin/env python3
"""批量DWG格式转换工具"""
import os
import sys
import subprocess
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
def convert_file(dwg_path, output_dir, output_format):
"""转换单个文件"""
filename = Path(dwg_path).stem
if output_format == 'dxf':
output_path = output_dir / f"{filename}.dxf"
cmd = ['dwg2dxf', str(dwg_path), '-o', str(output_path)]
elif output_format == 'json':
output_path = output_dir / f"{filename}.json"
cmd = ['dwgread', '-o', 'json', str(dwg_path)]
elif output_format == 'geojson':
output_path = output_dir / f"{filename}.geojson"
cmd = ['dwgread', '-o', 'geojson', str(dwg_path)]
elif output_format == 'svg':
output_path = output_dir / f"{filename}.svg"
cmd = ['dwg2SVG', str(dwg_path), '-o', str(output_path)]
else:
return False, f"不支持的格式: {output_format}"
try:
if output_format in ['json', 'geojson']:
with open(output_path, 'w') as f:
result = subprocess.run(cmd, stdout=f, stderr=subprocess.PIPE,
timeout=60)
else:
result = subprocess.run(cmd, capture_output=True, timeout=60)
if result.returncode == 0 and output_path.exists() and \
output_path.stat().st_size > 0:
return True, str(output_path)
else:
return False, result.stderr.decode() if result.stderr else "未知错误"
except subprocess.TimeoutExpired:
return False, "超时"
except Exception as e:
return False, str(e)
def batch_convert(input_dir, output_format, output_dir=None, max_workers=4):
"""批量转换"""
input_path = Path(input_dir)
if not input_path.exists():
print(f"输入目录不存在: {input_dir}")
return
if output_dir is None:
output_path = input_path / f"{output_format}_output"
else:
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
dwg_files = list(input_path.glob("*.dwg")) + list(input_path.glob("*.DWG"))
if not dwg_files:
print("没有找到DWG文件")
return
print(f"找到 {len(dwg_files)} 个DWG文件")
print(f"输出目录: {output_path}")
print(f"输出格式: {output_format}")
print("-" * 50)
success = 0
failed = 0
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {
executor.submit(convert_file, f, output_path, output_format): f
for f in dwg_files
}
for future in as_completed(futures):
dwg_file = futures[future]
try:
ok, msg = future.result()
if ok:
print(f"✓ {dwg_file.name}")
success += 1
else:
print(f"✗ {dwg_file.name}: {msg}")
failed += 1
except Exception as e:
print(f"✗ {dwg_file.name}: {e}")
failed += 1
print("-" * 50)
print(f"完成: 成功 {success}, 失败 {failed}")
if __name__ == '__main__':
if len(sys.argv) < 3:
print("用法: python batch_convert.py <输入目录> <格式> [输出目录]")
print("格式: dxf, json, geojson, svg")
sys.exit(1)
input_dir = sys.argv[1]
output_format = sys.argv[2]
output_dir = sys.argv[3] if len(sys.argv) > 3 else None
batch_convert(input_dir, output_format, output_dir)
9.7 版本转换
9.7.1 降级转换
# 将高版本DWG转换为R2000
dwgrewrite -r r2000 r2018_drawing.dwg -o r2000_drawing.dwg
# 通过DXF中转
dwg2dxf -r r14 drawing.dwg -o temp.dxf
dxf2dwg -r r2000 temp.dxf -o output.dwg
rm temp.dxf
9.7.2 版本转换脚本
#!/bin/bash
# version_convert.sh - DWG版本转换
TARGET_VERSION="${1:-r2000}"
shift
if [ $# -eq 0 ]; then
echo "用法: $0 <目标版本> <dwg文件...>"
echo "目标版本: r14, r2000, r2004, r2007, r2010"
exit 1
fi
for dwg in "$@"; do
if [ -f "$dwg" ]; then
output="${dwg%.dwg}_${TARGET_VERSION}.dwg"
echo "转换: $dwg -> $output"
dwgrewrite -r "$TARGET_VERSION" "$dwg" -o "$output"
fi
done
9.8 本章小结
本章介绍了LibreDWG的格式转换功能:
- DWG↔DXF:双向转换,支持ASCII和二进制DXF
- DWG↔JSON:完整数据表示,便于分析和处理
- DWG→GeoJSON:地理空间格式,适用于GIS
- DWG→SVG:矢量图形,适用于Web显示
- 批量转换:Shell和Python脚本实现
- 版本转换:不同DWG版本之间的转换
下一章预告:第10章 - Python绑定开发指南 - 使用Python进行LibreDWG开发。

浙公网安备 33010602011771号