Jump to content
You must now use your email address to sign in [click for more info] ×

Font sizes in imported SVG documents are sometimes interpreted incorrectly


efharkin

Recommended Posts

Description of bugs: Text in imported SVG documents is not rendered in the correct size or font family if the font was specified using the shorthand "font" property (eg "font: 10pt 'Arial'"), and font sizes seem to always be interpreted in points even if "px" units are specified.

Use case: I like to use Affinity Designer to make manual adjustments to scientific figures generated using the popular Python plotting library Matplotlib and saved as SVGs. It so happens that Matplotlib writes font information using the "font" property rather than "font-size" and "font-family". Affinity Designer is fantastic but has long had issues with rendering text from SVG files created by Matplotlib, so I often end up having to either save the SVG text as paths (making it non-editable) or save my figures as PNGs and use Affinity only for arranging them. I know at least a few people who are currently using Inkscape (which doesn't have this bug) and would consider switching to Affinity if this issue were fixed.

To reproduce: Open the minimal SVG file below in Affinity Designer and in a web browser. (See screenshots below; the one with consistent font sizes is from Firefox 106.0.5 and the other is from Affinity.)

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE svg>
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="200pt" height="125pt" viewBox="0 0 200 125" xmlns="http://www.w3.org/2000/svg" version="1.1">
    <text x="0" y="15" style="font: 7.5pt 'Arial'">font: 7.5pt 'Arial'</text>
    <text x="0" y="30" style="font: 10px 'Arial'">font: 10px 'Arial'</text>
    <text x="0" y="45" style="font: 7.5pt 'Helvetica'">font: 7.5pt 'Helvetica'</text>
    <text x="0" y="60" style="font: 10px 'Helvetica'">font: 10px 'Helvetica'</text>
    <text x="0" y="75" style="font-size: 7.5pt; font-family: 'Arial'">font-size: 7.5pt; font-family: 'Arial'</text>
    <text x="0" y="90" style="font-size: 10px; font-family: 'Arial'">font-size: 10px; font-family: 'Arial'</text>
    <text x="0" y="105" style="font-size: 7.5pt; font-family: 'Helvetica'">font-size: 7.5pt; font-family: 'Helvetica'</text>
    <text x="0" y="120" style="font-size: 10px; font-family: 'Helvetica'">font-size: 10px; font-family: 'Helvetica'</text>
</svg>

To test compatibility with Matplotlib directly, here's a minimal Python script. The screenshots are again from Firefox and Affinity.

import matplotlib as mpl
import matplotlib.pyplot as plt

# Export text as regular text instead of paths or using svgfonts
mpl.rcParams['svg.fonttype'] = 'none'

# Set font to a widely-available but non-default font to show that Affinity
# ignores font-family
mpl.rcParams['font.family'] = 'sans-serif'
mpl.rcParams['font.sans-serif'] = 'Helvetica'

# Make the figure 1in x 0.75in so that incorrect font sizes in Affinity are
# very obvious (text will badly overflow).
plt.figure(figsize=(1, 0.75))

plt.plot([1, 2, 3], [2, 1, 1], label='My line')
plt.ylabel('Y-axis label')
plt.xlabel('X-axis label')
plt.legend(loc='upper left', bbox_to_anchor=(1, 1))

plt.savefig('matplotlib-demo.svg', bbox_inches='tight')

System information:

  • MacOS 10.15.7 on Intel Macbook Pro
  • Affinity Designer 2.0.0
  • Turning hardware acceleration on and off doesn't affect problem
  • No unusual hardware or software

 

Screen Shot 2022-11-19 at 8.20.18 PM.png

Screen Shot 2022-11-19 at 8.21.02 PM.png

Screen Shot 2022-11-19 at 8.48.17 PM.png

Screen Shot 2022-11-19 at 8.48.45 PM.png

Edited by efharkin
Add example python script to reproduce
Link to comment
Share on other sites

In case anyone comes across this issue looking for a temporary fix, running SVGs created by Matplotlib through the script below produces usable results for me:

 

#!python
import re


def patch_affinity_svg(svg_text):
    """Patch Matplotlib SVG so that it can be read by Affinity Designer."""
    matches = [
        x for x in re.finditer('font: ([0-9.]+)px ([^;]+);', svg_text)
    ]
    svg_pieces = [svg_text[: matches[0].start()]]
    for i, match in enumerate(matches):
        # Change "font" style property to separate "font-size" and
        # "font-family" properties because Affinity ignores "font".
        font_size_px, font_family = match.groups()
        new_font_style = (
            f'font-size: {float(font_size_px):.1f}px; '
            f'font-family: {font_family};'
        )
        svg_pieces.append(new_font_style)
        if i < len(matches) - 1:
            svg_pieces.append(svg_text[match.end() : matches[i + 1].start()])
        else:
            svg_pieces.append(svg_text[match.end() :])
    return ''.join(svg_pieces)


if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('fname', help="Path to Matplotlib SVG file to patch.")
    parser.add_argument(
        '-o', '--output', help="Path to output patched file.", required=False
    )
    args = parser.parse_args()

    with open(args.fname, 'r') as f:
        svg_text = f.read()

    patched_svg = patch_affinity_svg(svg_text)

    if args.output is None:
        args.output = args.fname

    with open(args.output, 'w') as f:
        f.write(patched_svg)

 

Link to comment
Share on other sites

  • 2 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...

Important Information

Terms of Use | Privacy Policy | Guidelines | We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.