diff --git a/src/bonsai/bonsai/bim/module/drawing/svgwriter.py b/src/bonsai/bonsai/bim/module/drawing/svgwriter.py index 22a830031e..631ff21704 100644 --- a/src/bonsai/bonsai/bim/module/drawing/svgwriter.py +++ b/src/bonsai/bonsai/bim/module/drawing/svgwriter.py @@ -198,7 +198,10 @@ def parse_markdown_it(text: str) -> list[dict[str, Union[str, None]]]: class SvgWriter: metadata: list[str] - resource_paths: dict[tool.Drawing.ResourceType, Union[str, None]] + # Resources in MULTI_PATH_RESOURCES are stored as a list of paths, other resources as a single path. + resource_paths: dict[tool.Drawing.ResourceType, Union[str, list[str], None]] + # Resources that support multiple comma-separated paths. + MULTI_PATH_RESOURCES = ("Stylesheet",) def __init__( self, @@ -248,17 +251,28 @@ class SvgWriter: if not resource_path: self.resource_paths[resource] = None continue - resource_path = tool.Ifc.resolve_uri(resource_path) - os.makedirs(os.path.dirname(resource_path), exist_ok=True) - if not os.path.exists(resource_path): - resource_basename = os.path.basename(resource_path) - ootb_resource = tool.Blender.get_data_dir_path(Path("assets") / resource_basename) - print( - f"WARNING. Couldn't find {resource} for the drawing by the path: {resource_path}. Default BBIM resource will be copied from {ootb_resource}" - ) - if os.path.exists(ootb_resource): - shutil.copy(ootb_resource, resource_path) - self.resource_paths[resource] = resource_path + if resource in self.MULTI_PATH_RESOURCES: + # Paths need to be split before they're resolved, + # otherwise the entire string is resolved as a single path. + self.resource_paths[resource] = [ + self.prepare_resource_path(resource, p) for p in resource_path.split(",") if p.strip() + ] + continue + self.resource_paths[resource] = self.prepare_resource_path(resource, resource_path) + + def prepare_resource_path(self, resource: tool.Drawing.ResourceType, resource_path: str) -> str: + """Resolve resource path relative to the IFC file, copying the OOTB resource, if it's missing.""" + resource_path = tool.Ifc.resolve_uri(resource_path.strip()) + os.makedirs(os.path.dirname(resource_path), exist_ok=True) + if not os.path.exists(resource_path): + resource_basename = os.path.basename(resource_path) + ootb_resource = tool.Blender.get_data_dir_path(Path("assets") / resource_basename) + print( + f"WARNING. Couldn't find {resource} for the drawing by the path: {resource_path}. Default BBIM resource will be copied from {ootb_resource}" + ) + if os.path.exists(ootb_resource): + shutil.copy(ootb_resource, resource_path) + return resource_path def define_boilerplate(self): self.add_stylesheet() @@ -280,13 +294,17 @@ class SvgWriter: paths = self.resource_paths["Stylesheet"] if not paths: return - path_list = [p.strip() for p in paths.split(",")] - for path in path_list: + assert isinstance(paths, list) + # Stylesheets are concatenated in the order they're listed, so they cascade naturally. + css = [] + for path in paths: if not os.path.exists(path): print(f"WARNING. Couldn't find stylesheet for the drawing by the path: {path}") continue with open(path, "r") as stylesheet: - self.svg.defs.add(self.svg.style(stylesheet.read())) + css.append(stylesheet.read()) + if css: + self.svg.defs.add(self.svg.style("\n".join(css))) def add_markers(self): path = self.resource_paths["Markers"]