Skip to content

Auto-generate SF Symbol weight variants from stroke-width scaling#125

Open
rursache wants to merge 1 commit intoswhitty:mainfrom
rursache:feat/sfsymbol-stroke-variants
Open

Auto-generate SF Symbol weight variants from stroke-width scaling#125
rursache wants to merge 1 commit intoswhitty:mainfrom
rursache:feat/sfsymbol-stroke-variants

Conversation

@rursache
Copy link
Copy Markdown
Contributor

Fixes #38

Summary

Adds two new flags to the sfsymbol format that synthesize the ultralight and black weight variants directly from the regular SVG by scaling every stroke-width value before strokes get expanded into outlines:

  • --ultralight-stroke-width <scale> — typically 0.5 or 50%
  • --black-stroke-width <scale> — typically 2.0 or 200%

Both decimal (0.5) and percent (50%) syntax are accepted. The existing --ultralight <file> and --black <file> flags still work and take precedence; a warning is printed when both are supplied.

Implementation

  • SFSymbolRenderer.StrokeWidthScale: small value type with a permissive parser (rejects 0, negative, malformed, empty)
  • StrokeWidthScaler: walks a DOM.SVG and multiplies stroke-width on
    1. element attributes
    2. element inline style
    3. <style> rules in every StyleSheet
      Recurses through groups, clipPath/mask/pattern containers in defs, and the defs.elements dictionary. Returns the count of values mutated so the caller can warn when no strokes were found
  • SFSymbolRenderer.render(regular:ultralight:black:): when a scale is supplied without an explicit variant URL, re-parses the regular file (DOM nodes are reference types so a fresh parse is the cleanest deep clone), runs the scaler, and feeds the mutated DOM into the existing variant pipeline. No path/layer/codegen changes were needed
  • CLI: new Modifier cases, parseStrokeScale helper, threading through Configuration and processImage, updated --help text
  • README: documents the flags, syntax, the explicit-wins rule, the no-strokes warning, and notes that only stroke-width is scaled today (future work could cover dash arrays and similar)

Output emits these warnings to stderr:

  • --ultralight-stroke-width has no effect: source SVG has no stroke-width values.
  • explicit --ultralight overrides --ultralight-stroke-width.

Tests

RendererSFSymbolStrokeScaleTests (18 tests) covers the parser, the walker against each of the three stroke-width sources individually plus a mixed fixture, nested group recursion, clipPath children, the no-strokes case, and end-to-end runs through SFSymbolRenderer confirming the variant paths actually differ from regular when scaled. CommandLineConfigurationTests adds three tests for the new flags (valid decimal, valid percent, invalid value rejection).

Five new fixtures cover the stroke sources independently:

Fixture Source
stroke-attribute.svg element attribute
stroke-inline-style.svg inline style=
stroke-stylesheet.svg <style> selector
stroke-mixed.svg all three plus a group ancestor
stroke-none.svg no strokes (warning path)

Full suite: swift test — 217 tests pass, 0 failures.

Test plan

  • swift test passes locally
  • swiftdraw key.svg --format sfsymbol --ultralight-stroke-width 50% --black-stroke-width 2.0 produces a valid template
  • Warning prints when source has no strokes
  • Warning prints when explicit --ultralight and --ultralight-stroke-width are both supplied

Adds --ultralight-stroke-width and --black-stroke-width flags to the
sfsymbol format. When set without an explicit variant SVG, SwiftDraw
clones the regular DOM, multiplies every stroke-width value by the
scale (decimal or percent syntax), then runs that mutated DOM through
the existing variant pipeline. Explicit --ultralight or --black always
wins, with a warning when both are supplied.

Fixes swhitty#38
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

❌ Patch coverage is 94.98433% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.96%. Comparing base (8f03233) to head (8192cf6).

Files with missing lines Patch % Lines
...urces/Renderer/Renderer.SFSymbol+StrokeScale.swift 90.27% 7 Missing ⚠️
SwiftDraw/Sources/Renderer/Renderer.SFSymbol.swift 80.64% 6 Missing ⚠️
...Draw/Sources/CommandLine/CommandLine+Process.swift 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #125      +/-   ##
==========================================
+ Coverage   89.80%   89.96%   +0.16%     
==========================================
  Files         159      161       +2     
  Lines       15868    16181     +313     
==========================================
+ Hits        14251    14558     +307     
- Misses       1617     1623       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Autogenerate variants with Command Line Tool

1 participant