144 lines
6.1 KiB
Python
144 lines
6.1 KiB
Python
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}") |