Bonsai: fix comma-separated stylesheet paths only loading the last file (#9411)

Follow-up to cb08191. The comma-separated pset value was resolved by
tool.Ifc.resolve_uri() as a single path before add_stylesheet() split it,
so normpath collapsed the whole string down to the last entry.

Split the value into paths first, then resolve each one, and concatenate
the stylesheets in listed order into a single <style> element.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Ryan Schultz
2026-08-31 16:22:59 -05:00
committed by GitHub
parent 29e14c10cb
commit 971cccc186
@@ -198,7 +198,10 @@ def parse_markdown_it(text: str) -> list[dict[str, Union[str, None]]]:
class SvgWriter: class SvgWriter:
metadata: list[str] 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__( def __init__(
self, self,
@@ -248,17 +251,28 @@ class SvgWriter:
if not resource_path: if not resource_path:
self.resource_paths[resource] = None self.resource_paths[resource] = None
continue continue
resource_path = tool.Ifc.resolve_uri(resource_path) if resource in self.MULTI_PATH_RESOURCES:
os.makedirs(os.path.dirname(resource_path), exist_ok=True) # Paths need to be split before they're resolved,
if not os.path.exists(resource_path): # otherwise the entire string is resolved as a single path.
resource_basename = os.path.basename(resource_path) self.resource_paths[resource] = [
ootb_resource = tool.Blender.get_data_dir_path(Path("assets") / resource_basename) self.prepare_resource_path(resource, p) for p in resource_path.split(",") if p.strip()
print( ]
f"WARNING. Couldn't find {resource} for the drawing by the path: {resource_path}. Default BBIM resource will be copied from {ootb_resource}" continue
) self.resource_paths[resource] = self.prepare_resource_path(resource, resource_path)
if os.path.exists(ootb_resource):
shutil.copy(ootb_resource, resource_path) def prepare_resource_path(self, resource: tool.Drawing.ResourceType, resource_path: str) -> str:
self.resource_paths[resource] = resource_path """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): def define_boilerplate(self):
self.add_stylesheet() self.add_stylesheet()
@@ -280,13 +294,17 @@ class SvgWriter:
paths = self.resource_paths["Stylesheet"] paths = self.resource_paths["Stylesheet"]
if not paths: if not paths:
return return
path_list = [p.strip() for p in paths.split(",")] assert isinstance(paths, list)
for path in path_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): if not os.path.exists(path):
print(f"WARNING. Couldn't find stylesheet for the drawing by the path: {path}") print(f"WARNING. Couldn't find stylesheet for the drawing by the path: {path}")
continue continue
with open(path, "r") as stylesheet: 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): def add_markers(self):
path = self.resource_paths["Markers"] path = self.resource_paths["Markers"]