import os.path from lxml import etree font_size = 16 margin = 18 line_length = 16 text_vertical_margin = 8 color_main = '#0067b9' color_bg1 = '#c4e5ff' color_bg2 = '#ebf6ff' stroke_width = 3 class hdl_component(): @staticmethod def get_name(lib_name): return f"{lib_name.replace('/','-')}.svg" @staticmethod def render(env, lib_name, item): #ports, bus_interface dest_dir = os.path.join(env.srcdir, '_build/managed') dest_file = os.path.join(dest_dir, hdl_component.get_name(lib_name)) def make_style(parent): style = etree.SubElement(parent, 'style').text = """ a { text-decoration: none; } """ def make_gradient(parent): defs = etree.SubElement(parent, 'defs') gradient = etree.SubElement(defs, 'linearGradient', attrib={ 'id':'ip_background', 'x1':'0', 'x2':'1', 'y1':'0', 'y2':'1' }) etree.SubElement(gradient, 'stop', attrib={'offset':'0%', 'stop-color':color_bg1}) etree.SubElement(gradient, 'stop', attrib={'offset':'100%', 'stop-color':color_bg2}) def symbol_bus(parent): rect_height = font_size one_fifth_x = line_length/5 one_fifth_y = rect_height/5 x = 0; y = 0 pattern = [[0,1,0,1,0],[0,0,0,0,0],[0,1,0,1,0],[0,0,0,0,0],[0,1,0,1,0]] etree.SubElement(parent, "rect", attrib={ 'x':'0', 'y':'0', 'width':str(line_length), 'height':str(font_size), 'fill':color_bg1, }) for i in pattern: for j in i: if j: etree.SubElement(parent, "rect", attrib={ 'x':str(x), 'y':str(y), 'width':str(one_fifth_x), 'height':str(one_fifth_y), 'fill':color_main, }) x = x+one_fifth_x x = 0 y = y+one_fifth_y def symbol_port(parent): etree.SubElement(parent, "line", attrib={ 'stroke':'black', 'stroke-width':str(stroke_width), 'x1':'0', 'y1':str(font_size/2), 'x2':str(line_length), 'y2':str(font_size/2) }) def create_text(items, side): y_pos = margin*4 if side == 'out': text_anchor = 'end' x_pos = margin*4 + aux_width line_x1 = x_pos+(margin) line_x2 = x_pos+(margin+line_length) x_pos_group = x_pos+margin scale_group = 'scale(1,1)' else: text_anchor = 'start' x_pos = margin*3 line_x1 = x_pos-(margin+line_length) line_x2 = x_pos-(margin) x_pos_group = x_pos-margin scale_group = 'scale(-1,1)' for elem in items: if elem[1] == 'bus': link_anchor = f"#bus-interface-{elem[0]}" else: link_anchor = "#ports" link = etree.SubElement(root, "a", attrib={ 'href':link_anchor, }) etree.SubElement(link, "text", attrib={ 'style':f"font: {font_size}px sans-serif", 'text-anchor':text_anchor, 'dominant-baseline':'middle', 'x':str(x_pos), 'y':str(y_pos) }).text = elem[0] group = etree.SubElement(root, "g", attrib={ 'transform':f"translate({x_pos_group},{y_pos-font_size/2}) {scale_group}"} ) if elem[1] == 'bus': symbol_bus(group) else: symbol_port(group) y_pos += font_size+text_vertical_margin ins = [] outs = [] for key in item['bus_interface']: if item['bus_interface'][key]['role'] == 'master': outs.append((key, 'bus')) else: ins.append((key, 'bus')) for key in item['ports']: if item['ports'][key]['direction'] == 'out': outs.append((key, 'port')) else: ins.append((key, 'port')) max_len_in = 0 for elem in ins: max_len_in = len(elem[0]) if len(elem[0]) > max_len_in else max_len_in max_len_out = 0 for elem in outs: max_len_out = len(elem[0]) if len(elem[0]) > max_len_out else max_len_out aux_width = (max_len_in+max_len_out)*font_size*.6 num_outs = len(outs) num_ins = len(ins) max_num = max(num_outs, num_ins) root = etree.Element('svg', xmlns="http://www.w3.org/2000/svg") make_style(root) make_gradient(root) ip_width = aux_width + margin*3 ip_height = max_num*(font_size+text_vertical_margin) + margin*2 etree.SubElement(root, "rect", attrib={ 'x':str(margin*2), 'y':str(margin*2), 'width':str(ip_width), 'height':str(ip_height), 'rx':str(margin), 'fill':'url(#ip_background)' }) create_text(ins,'in') create_text(outs,'out') viewbox_x = margin*7 + aux_width viewbox_y = margin*7 + max_num*(font_size+text_vertical_margin) root.set('viewBox', f"0 0 {viewbox_x} {viewbox_y}") root.set('width', str(viewbox_x)) root.set('height', str(viewbox_y)) etree.SubElement(root, "rect", attrib={ 'x':str(margin*2), 'y':str(margin*2), 'width':str(ip_width), 'height':str(ip_height), 'rx':str(margin), 'fill':'none', 'stroke':color_main, 'stroke-width':str(stroke_width) }) ipname_y = viewbox_y-font_size-margin ipname_x = viewbox_x/2 etree.SubElement(root, "text", attrib={ 'style':f"font: {font_size}px sans-serif", 'fill': color_main, 'text-anchor':'middle', 'dominant-baseline':'middle', 'x':str(ipname_x), 'y':str(ipname_y) }).text = lib_name[lib_name.rfind('/')+1:] tree = etree.ElementTree(root) if not os.path.exists(dest_dir): os.makedirs(dest_dir) tree.write(dest_file) @staticmethod def render_placeholder(lib_name): root = etree.Element('svg', xmlns="http://www.w3.org/2000/svg") def text_element(text, fs, x, y, font='sans-serif'): etree.SubElement(root, "text", attrib={ 'style':f"font: {font_size*fs}px {font}", 'fill': '#666', 'text-anchor':'middle', 'dominant-baseline':'middle', 'x':str(x), 'y':str(y) }).text = text ip_width = font_size*30 ip_height = font_size*15 etree.SubElement(root, "rect", attrib={ 'x':str(margin*2), 'y':str(margin*2), 'width':str(ip_width), 'height':str(ip_height), 'rx':str(margin), 'stroke':'#666', 'fill':'none', 'stroke-width':str(stroke_width), 'stroke-dasharray':'8 16', 'stroke-linecap':'round' }) viewbox_x = ip_width + 4*margin viewbox_y = ip_height + 4*margin root.set('viewBox', f"0 0 {viewbox_x} {viewbox_y}") root.set('width', str(viewbox_x)) root.set('height', str(viewbox_y)) text_y = viewbox_y/2.5 text_x = viewbox_x/2 text_element("🧐", 4, text_x, text_y-text_vertical_margin*2) text_element(f"{lib_name[lib_name.rfind('/')+1:]} IP-XACT not found.", 1, text_x, text_y+font_size*2+text_vertical_margin) text_element("Generate it and the documentation:", .75, text_x, text_y+font_size*3+text_vertical_margin*2.5) text_element(f"(cd {lib_name}; make)", .75, text_x, text_y+font_size*4+text_vertical_margin*3, 'monospace') text_element("(cd docs; make html)", .75, text_x, text_y+font_size*5+text_vertical_margin*3.5, 'monospace') tree = etree.ElementTree(root) return etree.tostring(tree, encoding="utf-8", method="xml").decode("utf-8")