How to change figure size and other size elements for a svg file

279 Views Asked by At

I am currently working with figures that I save in SVG format, in order to include them into a HTML presentation. I'm looking for consistency in terms of fonts, line sizes etc .. across the presentation.

Hence, I need to scale all my figures of a factor 2 in width compared to the default matplotlib params.

For now, I am relying on an external python lib, svgutils:


import matplotlib.pyplot as plt
import svgutils.transform as sg

figname = 'test.svg'
fig = plt.figure()
fig.savefig(figname)

fig = sg.fromfile(figname)
newsize = ['{}pt'.format(2 * float(i.replace('pt', ''))) for i in fig.get_size()]

fig.set_size(newsize)
fig.save(figname)

but this is a small project and I am not sure it will be maintained through time, so I'd like to do it only with matplotlib.

If I only modify the figsize, all other elements (i.e lines) do not increase in size. Is there a way to scale all parameters? Or should I go over each element defined in the rcParams and scale them appropriately? That looks rather long to do ...

EDIT: Note that changing the DPI (as suggested here) will not work, as SVG objects do not have associated DPI, unlike raster formats (i.e png, jpeg, etc ..).

1

There are 1 best solutions below

4
On
  • Changing the DPI for SVGs from matplotlib will not work because SVGs are vector graphics and don't have a DPI in the same sense that raster graphics do.
  • To achieve a consistent scaling of all elements in the plot, you must update the figure size, and various size-related parameters in rcParams.
    • Manually adjust the values with rcParams, or use a scaling factor, as follows.
      • Some size parameters are text, 'large', which will be replaced with the corresponding numeric value, and then scaled.
  • Other resources:
  • Tested in python 3.12.0, matplotlib 3.8.0
# Check if value is numeric
def is_numeric(value):
    try:
        float(value)
        return True
    except ValueError:
        return False

# Set up a scaling factor
scale_factor = 2
text_num = {'large': 12, 'medium': 10}

# List of rcParams to scale
params_to_scale = [
    'font.size', 
    'axes.titlesize', 
    'axes.labelsize', 
    'xtick.labelsize',
    'ytick.labelsize', 
    'legend.fontsize', 
    'legend.handlelength',  
    'axes.linewidth',
    'grid.linewidth',
    'lines.linewidth', 
    'lines.markersize',
    'patch.linewidth',
    'xtick.major.width',
    'ytick.major.width',
    'xtick.minor.width',
    'ytick.minor.width',
]

# Scale the relevant rcParams by the scaling factor
for param in params_to_scale:
    current_value = plt.rcParams[param]
    print(f'Before {param}: {current_value}')
    if is_numeric(current_value):
        plt.rcParams[param] *= scale_factor
    else:
        # replace text value with numeric value and apply scaling
        plt.rcParams[param] = text_num[current_value] * scale_factor
    print(f'After {param}: {plt.rcParams[param]}\n')
    

# Create a sample plot to test
fig, ax = plt.subplots(figsize=(6 * scale_factor, 4 * scale_factor))
ax.plot([0, 1], [0, 1], label="Sample Line")
ax.set_title("Sample Title")
ax.set_xlabel("X Axis")
ax.set_ylabel("Y Axis")
ax.grid(True)
ax.legend()

# Save the figure
fig.savefig('scaled_figure.svg')

# Reset the rcParams back to default (optional)
plt.rcParams.update(plt.rcParamsDefault)

Printed Output

Before font.size: 10.0
After font.size: 20.0

Before axes.titlesize: large
After axes.titlesize: 24.0

Before axes.labelsize: medium
After axes.labelsize: 20.0

Before xtick.labelsize: medium
After xtick.labelsize: 20.0

Before ytick.labelsize: medium
After ytick.labelsize: 20.0

Before legend.fontsize: medium
After legend.fontsize: 20.0

Before legend.handlelength: 2.0
After legend.handlelength: 4.0

Before axes.linewidth: 0.8
After axes.linewidth: 1.6

Before grid.linewidth: 0.8
After grid.linewidth: 1.6

Before lines.linewidth: 1.5
After lines.linewidth: 3.0

Before lines.markersize: 6.0
After lines.markersize: 12.0

Before patch.linewidth: 1.0
After patch.linewidth: 2.0

Before xtick.major.width: 0.8
After xtick.major.width: 1.6

Before ytick.major.width: 0.8
After ytick.major.width: 1.6

Before xtick.minor.width: 0.6
After xtick.minor.width: 1.2

Before ytick.minor.width: 0.6
After ytick.minor.width: 1.2

scale_factor = 2

enter image description here

scale_factor = 1

enter image description here

Example images are png because svg is not supported on StackOverflow.