from bs4 import BeautifulSoup import cairo import cairosvg import re import io import os import subprocess class SvgCreator: # ERROR CODES: # self.svg_path = 0 : file not found # 2: textId not found -- 3: styles string not found -- 4: string not found def __init__(self, svg_path): #for Error-Handling try: with open(svg_path, "r", encoding="utf-8") as file: self.svg_path = file.read() except Exception as e: self.svg_path = 0 print(e) def update_text(self, text_id, new_text): # Parse the SVG content soup = BeautifulSoup(self.svg_path, "lxml-xml") # Find the text element and get the content text_element = soup.find("text", {"id": text_id}) if text_element == None: raise Exception("Inputfeld "+text_id+" nicht gefunden") class_attr = text_element.get("class") class_list = class_attr.split() text_element_content = text_element.getText() # Find the string starting with 'fnt' in text-element fnt_class = next((s for s in class_list if s.startswith('fnt')), None) if(fnt_class == None): raise Exception("SVG-Format Muser falsch") ###################################### # Find the string starting with '.fnt*' in styles-element input_string = self.svg_path match = re.search(r"\."+ fnt_class+ r"\s*\{[^\}]*\}", input_string) if match == None: raise Exception("SVG-Format Muser falsch") css_fnt_str = match.group() # extract the font-size from string font_size = re.search(r"font-size:(\d+\.?\d*)px;", css_fnt_str) if font_size == None: raise Exception("SVG-Format Muser falsch") font_size_ready = (float)(font_size.group(1)) #extract the font-type from string font_type = re.search(r"font-family:'(.*?)'", css_fnt_str) if font_type == None: raise Exception("SVG-Format Muser falsch") font_type_ready = font_type.group(1) ###################################### # Creating sandbox area to find out the metrics for manipulation later on # Convert SVG to PNG png_data = cairosvg.svg2png(self.svg_path.encode('utf-8')) # Create a Cairo image surface from the PNG data image_surface = cairo.ImageSurface.create_from_png(io.BytesIO(png_data)) # Create a Cairo context ctx = cairo.Context(image_surface) # Set the font size and font family # Insert extracted font_size_ready # # Insert extracted font_type_ready # Get the text extents ctx.set_font_size(font_size_ready) ctx.select_font_face(font_type_ready, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) extents = ctx.text_extents(text_element_content) # Get the text width and height from example string # Cal the dimensions if the new font-size it larger text_width_old = str(extents.width) text_height_old = extents.height dimension = (float)(text_width_old)*font_size_ready # set the new text. And check its width extents = ctx.text_extents(new_text) text_width_new = extents.width #calculate the font-size for the new name based on its width if text_width_new > (float)(text_width_old): # width is larger so its font-size gets smaller and its y-cor adapts to new size # Same sandBox principle as above new_font_size = dimension/text_width_new pp = cairo.Context(image_surface) pp.set_font_size(new_font_size) pp.select_font_face(font_type_ready, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) extents_pp = pp.text_extents(new_text) # Calc new y-cor text_height_new = extents_pp.height height_diff = (text_height_old - text_height_new) /2 text_element['y'] = str((float)(text_element['y']) - (height_diff)) # updating with new y-cor and old font-size new_css_fnt_str = css_fnt_str.replace("font-size:"+ str(font_size_ready)+"px", "font-size:"+ str(new_font_size)+"px") else: # width is smaller so the css style (font-size & and y-cor) stays new_css_fnt_str = css_fnt_str # Centering the new text by calc updated x-cor and setting it width_diff = ((float)(text_width_old) - (float)(extents.width))/2 text_element['x'] = str((float)(text_element['x']) + (width_diff)) # Update the text element content text_element.string = new_text # Update the CSS style in the SVG style_tag = soup.find("style") style_tag.string = style_tag.string.replace(css_fnt_str, new_css_fnt_str) # Convert the updated BeautifulSoup object back to a string new_svg = str(soup) new_svg = new_svg.replace(css_fnt_str, new_css_fnt_str) self.svg_path = new_svg return 1 def export_svg(self, export_path, export_name, dpi_value): # Save the modified SVG with open(export_path+export_name+".svg", "w") as file: file.write(self.svg_path) # Convert the modified SVG to a PNG image with the specified DPI cairosvg.svg2png(url=export_path+export_name+".svg", write_to=export_path+export_name+".png", dpi=dpi_value, unsafe=True) self.convert_svg_to_png(export_path+export_name+".svg", export_path+export_name+".png", dpi_value) # Remove the Junk-Data os.remove(export_path+export_name+".svg") return 1 def convert_svg_to_png(self, svg_path, png_path, dpi): try: subprocess.run(['C:\\Program Files\\Inkscape\\bin\\inkscape.exe', svg_path, '--export-filename', png_path, f'--export-dpi={dpi}']) print(f"Conversion successful: {svg_path} -> {png_path} at {dpi} DPI") except Exception as e: print(f"Conversion failed: {e}")