ソースを参照

Transférer les fichiers vers 'drawio2py'

adding a function to generate a library from a parsed library.
write_cell is a function in generate_diagram that is needed, for now, I copy past it, but maybe it would be better if write_cell and generate_style could be called outside of generate_diagram.
Léo Laffeach 2 年 前
コミット
757b00acad
1 ファイル変更203 行追加0 行削除
  1. 203 0
      drawio2py/generator.py

+ 203 - 0
drawio2py/generator.py

@@ -4,6 +4,7 @@ Functions for converting in-memory drawio data to XML.
 
 import xml.etree.ElementTree as ET
 from drawio2py.abstract_syntax import *
+from drawio2py.util import generate_empty_root_and_layer
 
 
 # generates XML tree and serializes it to a file (or any writeable object) 
@@ -114,8 +115,210 @@ def generate_diagram(page: Page) -> ET.Element:
 	write_cell(page.root)
 	return dia
 
+
+def generate_library(lib : Dict[str, Cell], file_object : str) -> None:
+	'''
+		Input:
+			- lib : Dict[str, Cell] \n
+			parsed library for drawio.
+			
+			- file_object : str \n
+			saving file. Must end with '.drawio'.
+
+		Output:
+			None
+
+		Create a drawio lib file from a parsed library.  
+	'''
+	# Verify if `path` end with ".drawio"
+	if not(file_object.endswith(".drawio") or file_object.endswith(".xml")):
+		raise Exception("The given path is not a correct file name for a drawio file.\nIt must end with '.drawio' or '.xml'.")
+
+	fileContent = "<mxlibrary>[\n"
+
+	keys = lib.keys()
+	nbKeys = len(keys)
+
+	def generate_style(style: Style) -> str:
+		res = ""
+		for k, v in style.data.items():
+			res += "%s=%s;" % (str(k), str(v))
+		return res
+
+	for idx, key in enumerate(keys):
+		
+		fileContent += '  {\n'
+		
+		# "xml"
+		mxgm = ET.Element("mxGraphModel")
+		root = ET.SubElement(mxgm, "root")
+
+		lib.get(key).properties["label"] = lib.get(key).properties.get("label", '').replace('<', '&lt;')
+		lib.get(key).properties["label"] = lib.get(key).properties.get("label", '').replace('>', '&gt;')
+		lib.get(key).properties["label"] = lib.get(key).properties.get("label", '').replace('"', '&quot;')
+
+		empty_root = generate_empty_root_and_layer()
+		empty_root.children[0].children.append(lib.get(key))
+
+
+		def write_cell(cell):
+			attrs = {}
+			if not cell.properties:
+				attrs["id"] = cell.id
+			if cell.value:
+				attrs["value"] = cell.value
+			if cell.style:
+				attrs["style"] = generate_style(cell.style)
+			if cell.parent:
+				attrs["parent"] = cell.parent.id
+			if isinstance(cell, Edge):
+				attrs["edge"] = "1"
+				if cell.source:
+					attrs["source"] = cell.source.id
+				if cell.target:
+					attrs["target"] = cell.target.id
+					
+			if cell.properties:
+				# Wrap in <object> if there are properties
+				properties_with_id = cell.properties.copy()
+				properties_with_id['id'] = cell.id  # <object> gets the ID, not the <mxCell> (WTF drawio!)
+				par = ET.SubElement(root, "object", properties_with_id)
+			else:
+				par = root
+
+			# Create the actual <mxCell>
+			c = ET.SubElement(par, "mxCell", attrs, **cell.attributes)
+
+			def optional_attributes(dict):
+				attrs = {}
+				for key, val in dict.items():
+					if val != None:
+						attrs[key] = str(val)
+				return attrs
+
+			def write_point(parent_xml, point: Point, as_str: Optional[str] = None):
+				attrs = optional_attributes({
+					"x": point.x,
+					"y": point.y,
+					"as": as_str,
+				})
+				return ET.SubElement(parent_xml, "mxPoint", attrs)
+
+			def write_geometry(parent_xml, geometry):
+				return ET.SubElement(parent_xml, "mxGeometry", optional_attributes({
+					"x": geometry.x,
+					"y": geometry.y,
+					"width": geometry.width,
+					"height": geometry.height,
+					"relative": "1" if geometry.relative else None,
+					"as": "geometry",
+				}))
+
+			# Geometry
+			if isinstance(cell, Vertex) or isinstance(cell, Edge):
+				g = write_geometry(c, cell.geometry)
+				if cell.geometry.offset != None:
+					write_point(g, cell.geometry.offset, "offset")
+				if isinstance(cell, Edge):
+					if len(cell.geometry.points) > 0:
+						a = ET.SubElement(g, "Array", {"as": "points"})
+						for p in cell.geometry.points:
+							write_point(a, p)
+					if cell.geometry.source_point is not None:
+						write_point(g, cell.geometry.source_point, "sourcePoint")
+					if cell.geometry.target_point is not None:
+						write_point(g, cell.geometry.target_point, "targetPoint")
+
+			for child in cell.children:
+				write_cell(child)
+
+		write_cell(empty_root)
+		
+		xml = ET.tostring(mxgm, encoding="unicode")
+		xml = xml.replace('<', '&lt;')
+		xml = xml.replace('>', '&gt;')
+		xml = xml.replace('"', '\\"')
+		fileContent += f'    "xml": "{xml}",\n'
+
+		# "w" & "h"
+		geometry = lib.get(key).geometry
+		geometry_class = type(geometry).__name__
+		if geometry_class == "VertexGeometry":
+			try:
+				w = int(geometry.width)
+			except ValueError:
+				try:
+					w = float(geometry.width)
+				except:
+					raise Exception("Invalid width value.")
+			except:
+				raise Exception("Invalid width value.")
+
+			if geometry.x != None:
+				try:
+					x = int(geometry.x)
+				except ValueError:
+					try:
+						x = float(geometry.x)
+					except:
+						raise Exception("Invalid x value.")
+				except:
+					raise Exception("Invalid x value.")
+				w += abs(x)
+
+			try:
+				h = int(geometry.height)
+			except ValueError:
+				try:
+					h = float(geometry.height)
+				except:
+					raise Exception("Invalid height value.")
+			except:
+				raise Exception("Invalid height value.")
+
+			if geometry.y != None:
+				try:
+					y = int(geometry.y)
+				except ValueError:
+					try:
+						y = float(geometry.y)
+					except:
+						raise Exception("Invalid y value.")
+				except:
+					raise Exception("Invalid y value.")
+				h += abs(y)
+
+		if geometry_class == "EdgeGeometry":
+			w = geometry.target_point.x
+			h = geometry.source_point.y
+				
+		fileContent += f'    "w": {w},\n    "h": {h},\n'
+		
+		# "aspect"
+		aspect = "fixed"
+		fileContent += f'    "aspect": "{aspect}",\n'
+
+		# "title"
+		title = key
+		fileContent += f'    "title": "{title}"\n'
+
+		fileContent += '  }'
+
+		if idx < nbKeys - 1:
+			fileContent += ',\n'
+
+	fileContent += "\n]</mxlibrary>"
+
+	# Saved it as a file at `path`
+	with open(file_object, "w") as file:
+		file.write(fileContent)
+
 if __name__ == '__main__':
 	from drawio2py.parser import Parser
+	from drawio2py.shapelib import parse_library
 
 	dfile = Parser.parse("../test.drawio")
 	generate(dfile, "../test2.drawio")
+
+	#d_lib_file = parse_library("../dlib/test.xml")
+	#generate_library(d_lib_file, "../dlib/test2.drawio")