Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Bug]: Exported SVG files are no longer imported Affinity Designer correctly #20910

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

Closed
tobywise opened this issue Aug 26, 2021 · 16 comments Β· Fixed by #28504
Closed

[Bug]: Exported SVG files are no longer imported Affinity Designer correctly #20910

tobywise opened this issue Aug 26, 2021 · 16 comments Β· Fixed by #28504

Comments

@tobywise
Copy link

Bug summary

When importing SVG files created by Matplotlib into Affinity Designer, text characters are imported on top of one another. There are no issues importing into Inkscape, and I have not tried any other software.

Code for reproduction

# Exporting .SVG using the following code
plt.savefig('filename.svg')

Actual outcome

matplotlib_affinity_text

Expected outcome

Matplotlib SVGs were imported into Affinity Designer correctly prior to #17669 - specifically this commit 7480805

Operating system

Windows

Matplotlib Version

3.4.3

Matplotlib Backend

No response

Python version

No response

Jupyter version

No response

Other libraries

No response

Installation

pip

Conda channel

No response

@tacaswell
Copy link
Member

Thank you for bisecting this back to the exact commit!

There are no issues importing into Inkscape,

This suggests that there is a bug in Affinity Designer, have you reported this to them? We have historically held that we will not work around bugs in down-stream renderers (rsvg eventually did fix their clipping box bug).

This does look consistent with all of the along-the-text-direciton offsets being off by 1/64. I think the question we need answered is if the svg files we are producing are complaint with the SVG spec or not. If we are, I think this has to be handled on the affinity side, if we are not then it is a bug on our side.

@tobywise
Copy link
Author

Thanks for looking at this - having done a bit more investigation it seems that the problem is that AD doesn't parse the text direction offsets in the <use> tag correctly. It looks like instead of applying the offset after scaling everything 1/64, it applies it before transforming, resulting in the offset being 1/64 what it should be.

As this seems to be a bug in AD, I'll report it to them and see what happens!

@tacaswell
Copy link
Member

@tobywise Do you have an xref to the issue with AD?

@tobywise
Copy link
Author

Here's my report on their bug reporting forum https://forum.affinity.serif.com/index.php?/topic/148539-bug-in-svg-imports/&tab=comments#comment-830429

@story645 story645 modified the milestones: unassigned, needs sorting Oct 6, 2022
@LorenzoPeri17
Copy link
Contributor

This seems to be still an issue in latest versions of matplotlib and Affinity Designer.
Generating SVGs and tweaking them in Designer is a key part of my workflow.
I've collected a couple of tricks I found in a small module.
The two main workarounds seem to be:

  • in text elements, replace font attributes with font-size and font-family (font is apparently ignored by AD),
  • in xlink:href replace x and y attributes with transform=translate(x y) (AD seems to ignore x or y attributes in hrefs).
    Patching those two attributes seems to usually fix the problems.

I'd be happy to contribute a PR (it should be a matter of changing no more than 10 lines in matplotlib/backends/backend_svg.py) if it is welcome :)

@timhoffm
Copy link
Member

timhoffm commented Jul 2, 2024

Thanks for the suggestion.

Do you have any documentation / specification links which prove that the suggested changes are equally valid (or ideally even preferred)? We should tailor our output to a specification not to what some individual program has implemented. Otherwise there's a risk of breaking the use in other programs.

@LorenzoPeri17
Copy link
Contributor

Thanks for the prompt reply! I agree that it is best to follow the spec rather than chase down bugs of individual editors. However, I think there might be an argument for both changes. I have done some digging in the docs and the mpl code, and here are my findings.

Font

While both ways are supported in CSS, the mozilla svg docs seem to separate font into its separate constituent properties. The w3 docs defines font as

Shorthand property for setting β€˜font-style’, β€˜font-variant’, β€˜font-weight’, β€˜font-size’, β€˜line-height’ and β€˜font-family’.

Since setting font rather than the separate properties is just a shorthand that may cause compatibility issues, I can see an argument for matplotlib doing the most correct thing and setting style attributes indvidually

xlink:href

Similar case for xlink:href, with the notable difference that it has been deprecated in favour of href without the xlink namespace in SVG2. Looking at the w3 docs for the layout of the use tag (5.6.2), there seems to be a lot of historical baggage on how it's handled, but x and y attributes get transofmed into a translate(x y) in the end. There seems to be even more subtleties when x and y are used within a g tag.

To me this issue seems more of an Affinity shortcoming (it seems to work fine on inkscape and most browsers). However, this is the behaviour people are most likely to run into because it is the default way in which the svg backend currently renders axes lables and ticklables.

More specifically, current the svg backend in RendererSVG._draw_text_as_path defines layouts using x and y if it does not contain any latex (ismath = False)

# if not ismath:
    attrib = {'xlink:href': f'#{glyph_id}'}
    if xposition != 0.0:
        attrib['x'] = _short_float_fmt(xposition)
     if yposition != 0.0:
        attrib['y'] = _short_float_fmt(yposition)
    writer.element('use', attrib=attrib)

while it uses tranfsorm=translate(x y) if ismath is True

# else:
    writer.element(
   'use',
   transform=_generate_transform([
   ('translate', (xposition, yposition)),
   ('scale', (scale,)),
   ]),
    attrib={'xlink:href': f'#{char_id}'})

Since both behaviours are equivalent according to the docs and present in the current matplotlib implementation, I could see an argument to have translate in both for the sake of compatibility.

Let me know your thoughs! :)

@timhoffm
Copy link
Member

timhoffm commented Jul 2, 2024

Thanks for the reseach work. This sounds reasonable to me and I'll support a change. However, I'd like to have a second opinion from another core dev.

@tacaswell
Copy link
Member

I think there are 3 proposed changes:

  • xlink:href -> href. It looks like it was deprecated in svg2, but svg2 is still a draft. My read is we should hold off on that for now?
  • using transform= in both branches seems reasonable to me
  • splitting font parameters back up seems reasonable (I think we somewhat recently merged them together)

@tacaswell tacaswell modified the milestones: future releases, v3.10.0 Jul 2, 2024
@LorenzoPeri17
Copy link
Contributor

Sounds good to me! I'm happy to hold off on xlink:href, also because that does not seem to cause issues with any SVG app. I'll get working on a PR for the other two changes since you both seem happy with them :)

@QuLogic
Copy link
Member

QuLogic commented Jul 2, 2024

splitting font parameters back up seems reasonable (I think we somewhat recently merged them together)

They were merged in 3.5: #19253

@tacaswell
Copy link
Member

I have a slight worry that there is a bug the other way where some program does not like the many-entry. I have a vague memory of some svg tests where the weight was wrong when generating the baseline on some platforms that we fixed by re-generating to get the single-string version of the font.

@QuLogic
Copy link
Member

QuLogic commented Jul 3, 2024

That was a special-casing in our converter, not the external program: effefaf

@tacaswell
Copy link
Member

ah, I am glad I miss-remember there!

@LorenzoPeri17
Copy link
Contributor

I found some time to work on the PR and update the check in the converter higlighted before. Some tests failed on azure because of a timeout on the interactive backend (which I don't think should be affected by the changes?), but all the svg-related tests seem to be passing, and now all the test images open correctly in the latest version of Affinity Designer 2.

@tobywise
Copy link
Author

@LorenzoPeri17 just wanted to say thank you for figuring out a fix for this - I've been using awkward workarounds for the past 3 years!

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

Successfully merging a pull request may close this issue.

6 participants