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:
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"]