Visitar URL original
fix(oxvg_optimiser): keep defaults used by attribute selectors in removeUnknownsAndDefaults by MaxFreedomPollard · Pull Request #326 · noahbald/oxvg · GitHub
Skip to content

fix(oxvg_optimiser): keep defaults used by attribute selectors in removeUnknownsAndDefaults - #326

Merged
noahbald merged 1 commit into
noahbald:mainfrom
MaxFreedomPollard:fix/keep-selected-defaults
Oct 25, 2026
Merged

noahbald merged 1 commit into
noahbald:mainfrom
MaxFreedomPollard:fix/keep-selected-defaults

Conversation

@MaxFreedomPollard

Copy link
Copy Markdown
Contributor

Description

removeUnknownsAndDefaults removes an attribute whose value is the default even when a <style> rule selects elements by that attribute. Once the attribute is gone the rule stops matching, so the document changes:

<svg xmlns="http://www.w3.org/2000/svg">
  <style>[preserveAspectRatio]{fill:red}</style>
  <svg viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
    <rect width="10" height="10"/>
  </svg>
</svg>

preserveAspectRatio="xMidYMid meet" equals the default, so the job removes it, [preserveAspectRatio] no longer matches the inner <svg>, and the rect loses its inherited red fill. SVGO has kept such defaults since svg/svgo#2144 (released in 4.0.1). Its fixture test/plugins/removeUnknownsAndDefaults.17.svg.txt is this case.

The default-value check in remove_unknown_and_default_attrs now also requires that no attribute selector in the document's stylesheets names the attribute. It reuses AttrStylesheet, which removeDeprecatedAttrs already builds for the same purpose. I made it pub(crate) and left it in remove_deprecated_attrs.rs; I can move it to utils if you prefer. As in SVGO, only the default-value removal is guarded. Unknown attributes and useless overrides behave as before. AttrStylesheet::extract runs once per element, as it already does in removeDeprecatedAttrs.

AttrStylesheet recorded [name] (exists) selectors by local_name_lower, so a camelCase name never matched: [preserveAspectRatio] was stored as preserveaspectratio. SVG attribute names are case-sensitive. The [name=value] and namespaced arms, and inlineStyles, already use local_name, so the exists arm now does too. Without this the fix above would not cover SVGO's fixture. It also lets removeDeprecatedAttrs with removeAny keep a viewTarget that a stylesheet selects, as SVGO's removeDeprecatedAttrs does.

Motivation and Context

The job's docs say it "should never visually change the document", and OXVG tracks SVGO 4.1.0, which includes this fix. There is no open issue for it. I found it by comparing the job with SVGO's plugin and test fixtures, so the title carries no issue number.

How Has This Been Tested?

Two snapshot cases are added:

  • remove_unknowns_and_defaults-18: SVGO's fixture 17 with the path replaced by a rect. preserveAspectRatio is kept (written as xMidYMid, since meet is the default), and the rect's unselected x="0" and y="0" defaults are still removed.
  • remove_attrs-9 (removeDeprecatedAttrs with removeAny): a <view> whose viewTarget is selected by [viewTarget] keeps it.

This change was not compiled locally, and no tests were run: there was no Rust toolchain in the environment I worked in. I traced the behaviour by reading the code. The two .snap files were written by hand from what the serializer produces in existing snapshots: convert_shape_to_path-7 for the <style> block, remove_attrs-2 for the <view> line, and remove_unknowns_and_defaults-17 for the removed x/y. CI is therefore the first build and test run of this change. No existing snapshot should change: the only existing test stylesheet with attribute selectors in these two jobs (remove_attrs-8) uses lowercase names.

Types of changes

Fixes

  • removeUnknownsAndDefaults keeps an attribute with its default value when an attribute selector in a <style> element names it, as SVGO does.
  • removeDeprecatedAttrs and removeUnknownsAndDefaults match [name] selectors for camelCase attributes such as viewTarget and preserveAspectRatio.

Features

Breaking changes

Checklist:

  • My code follows the code style of this project. (cargo check was not run locally, see above)
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

🤖 Generated with Claude Code

…oveUnknownsAndDefaults

removeUnknownsAndDefaults removed an attribute set to its default value
even when a stylesheet rule selects elements by that attribute, so
`[preserveAspectRatio]{fill:red}` stopped matching once
`preserveAspectRatio="xMidYMid meet"` was dropped. SVGO keeps these
defaults since svg/svgo#2144 (test/plugins/removeUnknownsAndDefaults.17.svg.txt).

The default-value removal now skips attributes that an attribute
selector names, using the AttrStylesheet that removeDeprecatedAttrs
already builds for the same purpose.

AttrStylesheet recorded `[name]` selectors by their lowercased name, so
camelCase attributes such as preserveAspectRatio or viewTarget never
matched. It now uses the name as written, like its `[name=value]` and
namespaced arms and like inlineStyles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@noahbald
noahbald merged commit ae9f6d7 into noahbald:main Oct 25, 2026
44 checks passed
@noahbald

Copy link
Copy Markdown
Owner

Thanks max for the contribution!

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.

2 participants