Skip to content

Add basic <symbol> support in SVG #1819

Open
Theo1335 wants to merge 12 commits intopy-pdf:masterfrom
Theo1335:svg-feature
Open

Add basic <symbol> support in SVG #1819
Theo1335 wants to merge 12 commits intopy-pdf:masterfrom
Theo1335:svg-feature

Conversation

@Theo1335
Copy link
Copy Markdown

@Theo1335 Theo1335 commented Apr 15, 2026

Add basic support in SVG parsing

This PR adds support for SVG <symbol> elements.

  • Reuse existing group parsing logic for symbols.
  • Add a unit test covering symbol parsing and cross-reference registration.

Checklist:

  • A unit test is covering the code added / modified by this PR

  • In case of a new feature, docstrings have been added, with also some documentation in the docs/ folder (N/A)

  • A mention of the change is present in CHANGELOG.md

  • This PR is ready to be merged

By submitting this pull request, I confirm that my contribution is made under the terms of the GNU LGPL 3.0 license.

@andersonhc
Copy link
Copy Markdown
Collaborator

Thank you for this PR @Theo1335 , it's looking very promising!

I found one problem testing this SVG

image

The symbols are rendered but the scale is wrong.

Can you please check how to fix this?

@Theo1335
Copy link
Copy Markdown
Author

Theo1335 commented Apr 21, 2026

Thank you for your feedback @andersonhc !

I think I've finally solved the scaling issue with <symbol> elements: the viewBox is now extracted and stored when <symbol> is parsed, and when a <use> directive references a symbol with a width and height, the appropriate scaling is applied based on the `viewBox's dimensions.

Please let me know what you think.

Comment thread fpdf/svg.py
Comment on lines +1515 to +1520
viewbox = symbol.attrib.get("viewBox")
if viewbox:
parts = viewbox.replace(",", " ").split()
if len(parts) >= 4:
setattr(group, "_vw", float(parts[2]))
setattr(group, "_vh", float(parts[3]))
Copy link
Copy Markdown
Collaborator

@andersonhc andersonhc Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the test is failing:

    @force_nodocument
    def build_symbol(self, symbol: "Element") -> GraphicsContext:
        """Parse <symbol> as reusable content, not rendered directly."""
        group = self.build_group(symbol)
        viewbox = symbol.attrib.get("viewBox")
        if viewbox:
            parts = viewbox.replace(",", " ").split()
            if len(parts) >= 4:
>               setattr(group, "_vw", float(parts[2]))
E               AttributeError: 'GraphicsContext' object has no attribute '_vw'

I don't think adding the viewbox attribute to the GraphicsContext is the best approach.
I recommend looking into using the GaphicsContext's transform to apply the viewbox.

Apply translate(-vx, -vy) @ scale(1 / vw, 1 / vh) to the symbol group should make it normalized to unit coordinates. In build_xref(), apply scale(width, height) @ translate(x, y).

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