Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[experiment] Use DOM parser for SVG exports #3020

Closed
wants to merge 15 commits into from
Closed

Conversation

steveruizok
Copy link
Collaborator

This PR gradually moves our shapes' toSvg methods to use DOM parsing instead of manually creating elements.

Pros:

  • less code
  • more readable code
  • general refactoring

Cons:

  • theoretically slower, though everything I've read suggests the differences only occur at high volumes

Change Type

  • minor — New feature

Test Plan

  1. Compare SVG exports before / after

Release Notes

  • Fixes a bug with SVG export on vertical arrows with labels

@huppy-bot huppy-bot bot added the minor Increment the minor version when merged label Mar 2, 2024
Copy link

vercel bot commented Mar 2, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
examples ✅ Ready (Inspect) Visit Preview Mar 2, 2024 4:54pm
1 Ignored Deployment
Name Status Preview Updated (UTC)
tldraw-docs ⬜️ Ignored (Inspect) Visit Preview Mar 2, 2024 4:54pm

@steveruizok
Copy link
Collaborator Author

Regarding the warnings above:

  • Do we care about XSS for SVG exports?
  • Is our validation robust enough to permit this?

@steveruizok
Copy link
Collaborator Author

Weirdly this requires writing namespaces by hand?

packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx Dismissed Show dismissed Hide dismissed
packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx Dismissed Show dismissed Hide dismissed
packages/tldraw/src/lib/shapes/frame/FrameShapeUtil.tsx Dismissed Show dismissed Hide dismissed
packages/tldraw/src/lib/shapes/frame/FrameShapeUtil.tsx Dismissed Show dismissed Hide dismissed
packages/tldraw/src/lib/shapes/geo/GeoShapeUtil.tsx Dismissed Show dismissed Hide dismissed
packages/tldraw/src/lib/shapes/image/ImageShapeUtil.tsx Dismissed Show dismissed Hide dismissed
packages/tldraw/src/lib/shapes/shared/ShapeFill.tsx Dismissed Show dismissed Hide dismissed
packages/tldraw/src/lib/shapes/shared/ShapeFill.tsx Dismissed Show dismissed Hide dismissed
packages/tldraw/src/lib/shapes/video/VideoShapeUtil.tsx Dismissed Show dismissed Hide dismissed
packages/tldraw/src/lib/shapes/video/VideoShapeUtil.tsx Dismissed Show dismissed Hide dismissed
@steveruizok
Copy link
Collaborator Author

It looks like we'll have to add the namespace ourselves if we want this to work.

@steveruizok
Copy link
Collaborator Author

Another thing we could do is make the toSvg return a string, and combine the strings ourselves in the editor's getSvg method. At the moment we're constructive an svg element, then converting it to a string anyway before doing anything else with it.

@steveruizok steveruizok changed the title [refactor] Use DOM parser for SVG exports [experiment] Use DOM parser for SVG exports Mar 7, 2024
github-merge-queue bot pushed a commit that referenced this pull request Mar 25, 2024
## Migration path
1. If any of your shapes implement `toSvg` for exports, you'll need to
replace your implementation with a new version that returns JSX (it's a
react component) instead of manually constructing SVG DOM nodes
2. `editor.getSvg` is deprecated. It still works, but will be going away
in a future release. If you still need SVGs as DOM elements rather than
strings, use `new DOMParser().parseFromString(svgString,
'image/svg+xml').firstElementChild`

## The change in detail
At the moment, our SVG exports very carefully try to recreate the
visuals of our shapes by manually constructing SVG DOM nodes. On its own
this is really painful, but it also results in a lot of duplicated logic
between the `component` and `getSvg` methods of shape utils.

In #3020, we looked at using string concatenation & DOMParser to make
this a bit less painful. This works, but requires specifying namespaces
everywhere, is still pretty painful (no syntax highlighting or
formatting), and still results in all that duplicated logic.

I briefly experimented with creating my own version of the javascript
language that let you embed XML like syntax directly. I was going to
call it EXTREME JAVASCRIPT or XJS for short, but then I noticed that we
already wrote the whole of tldraw in this thing called react and a (imo
much worse named) version of the javascript xml thing already existed.

Given the entire library already depends on react, what would it look
like if we just used react directly for these exports? Turns out things
get a lot simpler! Take a look at lmk what you think

This diff was intended as a proof of concept, but is actually pretty
close to being landable. The main thing is that here, I've deliberately
leant into this being a big breaking change to see just how much code we
could delete (turns out: lots). We could if we wanted to make this
without making it a breaking change at all, but it would add back a lot
of complexity on our side and run a fair bit slower

---------

Co-authored-by: huppy-bot[bot] <128400622+huppy-bot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
minor Increment the minor version when merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant