Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type 1 fonts with log graphs

I'm trying to use Matplotlib graphs as part of a camera-ready submission, and the publishing house requires the use of Type 1 fonts only.

I'm finding that the PDF backend happily outputs Type-1 fonts for simple graphs with linear Y axes, but outputs Type-3 fonts for logarithmic Y axes.

Using a logarithmic yscale incurs the use of mathtext, which seems to use Type 3 fonts, presumably because of the default use of exponential notation. I can use an ugly hack to get around this - using pyplot.yticks() to force the axis ticks to not use exponents - but this would require moving the plot region to accommodate large labels (like 10 ^ 6) or writing the axes as 10, 100, 1K, etc. so they fit.

I've tested the example below with the matplotlib master branch as of today, as well as 1.1.1, which produces the same behavior, so I don't know that this is a bug, probably just unexpected behavior.

#!/usr/bin/env python
# Simple program to test for type 1 fonts. 
# Generate a line graph w/linear and log Y axes.

from matplotlib import rc, rcParams

rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']})
#rc('font',**{'family':'sans-serif','sans-serif':['computer modern sans serif']})

# These lines are needed to get type-1 results:
# http://nerdjusttyped.blogspot.com/2010/07/type-1-fonts-and-matplotlib-figures.html
rcParams['ps.useafm'] = True
rcParams['pdf.use14corefonts'] = True
rcParams['text.usetex'] = False

import matplotlib.pyplot as plt

YSCALES = ['linear', 'log']

def plot(filename, yscale):
    plt.figure(1)
    xvals = range(1, 2)
    yvals = xvals
    plt.plot(xvals, yvals)
    plt.yscale(yscale)
    plt.savefig(filename + '.pdf')

if __name__ == '__main__':
    for yscale in YSCALES:
        plot('linegraph-' + yscale, yscale)

Does anyone know a clean way to get Type 1 fonts with log axes?

Thanks!

like image 591
user1784469 Avatar asked Oct 30 '12 03:10

user1784469


2 Answers

This is the code I use for camera-ready submissions:

from matplotlib import pyplot as plt

def SetPlotRC():
    #If fonttype = 1 doesn't work with LaTeX, try fonttype 42.
    plt.rc('pdf',fonttype = 1)
    plt.rc('ps',fonttype = 1)

def ApplyFont(ax):

    ticks = ax.get_xticklabels() + ax.get_yticklabels()

    text_size = 14.0

    for t in ticks:
        t.set_fontname('Times New Roman')
        t.set_fontsize(text_size)

    txt = ax.get_xlabel()
    txt_obj = ax.set_xlabel(txt)
    txt_obj.set_fontname('Times New Roman')
    txt_obj.set_fontsize(text_size)

    txt = ax.get_ylabel()
    txt_obj = ax.set_ylabel(txt)
    txt_obj.set_fontname('Times New Roman')
    txt_obj.set_fontsize(text_size)

    txt = ax.get_title()
    txt_obj = ax.set_title(txt)
    txt_obj.set_fontname('Times New Roman')
    txt_obj.set_fontsize(text_size)

The fonts won't appear until you run savefig

Example:

import numpy as np

SetPlotRC()

t = np.arange(0, 2*np.pi, 0.01)
y = np.sin(t)

plt.plot(t,y)
plt.xlabel("Time")
plt.ylabel("Signal")
plt.title("Sine Wave")

ApplyFont(plt.gca())
plt.savefig("sine.pdf")
like image 62
DrRobotNinja Avatar answered Nov 18 '22 02:11

DrRobotNinja


The preferred method to get Type 1 fonts via matplotlib seems to be to use TeX for typesetting. Doing so results in all axis being typeset in the default math font, which is typically undesired but which can be avoided by using TeX-commands.

Long story short, I found this solution:

import matplotlib.pyplot as mp
import numpy as np

mp.rcParams['text.usetex'] = True #Let TeX do the typsetting
mp.rcParams['text.latex.preamble'] = [r'\usepackage{sansmath}', r'\sansmath'] #Force sans-serif math mode (for axes labels)
mp.rcParams['font.family'] = 'sans-serif' # ... for regular text
mp.rcParams['font.sans-serif'] = 'Helvetica, Avant Garde, Computer Modern Sans serif' # Choose a nice font here

fig = mp.figure()
dim = [0.1, 0.1, 0.8, 0.8]

ax = fig.add_axes(dim)
ax.text(0.001, 0.1, 'Sample Text')
ax.set_xlim(10**-4, 10**0)
ax.set_ylim(10**-2, 10**2)
ax.set_xscale("log")
ax.set_yscale("log")
ax.set_xlabel('$\mu_0$ (mA)')
ax.set_ylabel('R (m)')
t = np.arange(10**-4, 10**0, 10**-4)
y = 10*t

mp.plot(t,y)

mp.savefig('tmp.png', dpi=300)

Then results in this resulting image

Inspired by: https://stackoverflow.com/a/20709149/4189024 and http://wiki.scipy.org/Cookbook/Matplotlib/UsingTex

like image 31
wsj Avatar answered Nov 18 '22 00:11

wsj