dxfedit/03_Python_OpenSource_DXF/extract_entities_to_json.py
2025-09-09 18:42:30 +08:00

130 lines
4.8 KiB
Python

import ezdxf
import json
import os
import argparse
# Mappings from ezdxf integer enums to human-readable strings
MTEXT_ALIGNMENT_MAP = {
1: "TOP_LEFT", 2: "TOP_CENTER", 3: "TOP_RIGHT",
4: "MIDDLE_LEFT", 5: "MIDDLE_CENTER", 6: "MIDDLE_RIGHT",
7: "BOTTOM_LEFT", 8: "BOTTOM_CENTER", 9: "BOTTOM_RIGHT",
}
TEXT_HALIGN_MAP = {0: "LEFT", 1: "CENTER", 2: "RIGHT", 4: "MIDDLE"}
TEXT_VALIGN_MAP = {0: "BASELINE", 1: "BOTTOM", 2: "MIDDLE", 3: "TOP"}
def get_text_alignment(text_entity):
""" Determines the alignment string from a TEXT or MTEXT entity. """
if text_entity.dxftype() == 'MTEXT':
return MTEXT_ALIGNMENT_MAP.get(text_entity.dxf.attachment_point, "TOP_LEFT")
elif text_entity.dxftype() == 'TEXT':
# For TEXT, alignment is determined by halign and valign.
# The default (0, 0) is LEFT, BASELINE which we treat as BOTTOM_LEFT.
# If halign or valign is non-default, the align_point is used instead of insert.
halign = TEXT_HALIGN_MAP.get(text_entity.dxf.halign, "LEFT")
valign = TEXT_VALIGN_MAP.get(text_entity.dxf.valign, "BOTTOM")
if valign == "BASELINE": # Treat BASELINE as BOTTOM for simplicity
valign = "BOTTOM"
# Combine them, e.g., "MIDDLE_CENTER"
# For simple left-aligned text, it will be "BOTTOM_LEFT"
if halign == "LEFT":
return f"{valign}_LEFT"
elif halign == "RIGHT":
return f"{valign}_RIGHT"
else: # CENTER or MIDDLE
return f"{valign}_CENTER"
return "BOTTOM_LEFT" # Default fallback
def extract_dxf_entities(dxf_path):
"""
Extracts all LINE and TEXT/MTEXT entities from a DXF file and returns them
as a dictionary structured for JSON output.
Args:
dxf_path (str): The full path to the DXF file.
Returns:
dict: A dictionary containing lists of extracted line and text entities,
or None if the file cannot be processed.
"""
try:
doc = ezdxf.readfile(dxf_path)
msp = doc.modelspace()
except (IOError, ezdxf.DXFStructureError) as e:
print(f"Error reading DXF file at {dxf_path}: {e}")
return None
extracted_data = {
"lines": [],
"texts": []
}
# --- Extract LINE entities ---
for line in msp.query('LINE'):
extracted_data["lines"].append({
"start": (line.dxf.start.x, line.dxf.start.y),
"end": (line.dxf.end.x, line.dxf.end.y),
"layer": line.dxf.layer,
"color": line.dxf.color,
"linetype": line.dxf.linetype
})
# --- Extract TEXT and MTEXT entities ---
for text_entity in msp.query('TEXT MTEXT'):
alignment = get_text_alignment(text_entity)
if text_entity.dxftype() == 'TEXT':
text_content = text_entity.dxf.text
height = text_entity.dxf.height
# If text is aligned, its true position is align_point, not insert
if text_entity.dxf.halign != 0 or text_entity.dxf.valign != 0:
insert_point = (text_entity.dxf.align_point.x, text_entity.dxf.align_point.y)
else:
insert_point = (text_entity.dxf.insert.x, text_entity.dxf.insert.y)
elif text_entity.dxftype() == 'MTEXT':
text_content = text_entity.text # MTEXT uses a 'text' attribute
height = text_entity.dxf.char_height
insert_point = (text_entity.dxf.insert.x, text_entity.dxf.insert.y)
else:
continue
extracted_data["texts"].append({
"content": text_content,
"insert_point": insert_point,
"alignment": alignment,
"height": height,
"style": text_entity.dxf.style,
"layer": text_entity.dxf.layer,
"color": text_entity.dxf.color
})
return extracted_data
def main():
parser = argparse.ArgumentParser(description="Extract entities (lines, texts, mtexts) from a DXF file and save them to a JSON file.")
parser.add_argument("source_dxf", help="Path to the source DXF file.")
parser.add_argument("output_json", help="Path to the output JSON file.")
args = parser.parse_args()
print(f"Extracting entities from: {args.source_dxf}")
extracted_data = extract_dxf_entities(args.source_dxf)
if extracted_data:
try:
with open(args.output_json, 'w', encoding='utf-8') as f:
json.dump(extracted_data, f, ensure_ascii=False, indent=2)
print(f"Successfully extracted {len(extracted_data.get('lines', []))} lines and "
f"{len(extracted_data.get('texts', []))} text entities to: {args.output_json}")
except IOError as e:
print(f"Error: Could not write to output file {args.output_json}. Reason: {e}")
if __name__ == "__main__":
main()