I want to create a multi-page PDF file for printing using python. I would prefer to do this with a library that could work well in any environment so I tried with matplotlib.
I can produce the desired figure of A4 size, and create a multi-page PDF file, but the figure artist shrinks and does not occupy the whole page.
I can either produce a PDF of A4-sized pages with padding around the figure, or a PDF file with smaller page size (the page shrinks around the figure to remove the padding, with the figure remaining the same size).
Can someone help?
(I don't think this is duplicated, there are other questoions about the padding but not with the same aim as I have and I could not see any answer that worked for me - I have been searching around for days!)
What I have tried so far following a few SO questions/answers is:
bbox_inches='tight', pad_inches=0
in the pyplot.savefig()
function, which gives a smaller than A4 PDF page with no padding.(Strangely, this works well with EPS format, which prints straight into real size in A4 paper, but I don't know how to do a multi-page file out of that.)
If I do not use bbox_inches='tight', pad_inches=0
, the figure size seems more or less the same but the padding around fills the space to make an A4 page.
Using fig.tight_layout(rect=[0,0,1,1])
or fig.tight_layout(pad=0)
, which keeps the padding.
This example code produces the figure in EPS (A4 no padding), PDF (A4 with padding) and TIFF (no padding but smaller size, so when you print it has padding):
import matplotlib.pyplot as plt
from matplotlib import gridspec
def plot_form_page():
# A4 canvas
fig_width_cm = 21 # A4 page
fig_height_cm = 29.7
inches_per_cm = 1 / 2.54 # Convert cm to inches
fig_width = fig_width_cm * inches_per_cm # width in inches
fig_height = fig_height_cm * inches_per_cm # height in inches
fig_size = [fig_width, fig_height]
plt.rc('text', usetex=False) # so that LaTeX is not needed when creating a PDF with PdfPages later on
fig = plt.figure()
fig.set_size_inches(fig_size)
fig.set_facecolor('#9999ff')
gs = gridspec.GridSpec(29, 21, wspace=0.1, hspace=0.1)
# external axis
ax0 = fig.add_subplot(gs[:, :])
# for side in ["top", "bottom", "left", "right"]:
# ax0.spines[side].set_visible(False)
# header
ax1 = fig.add_subplot(gs[1:4, 1:4])
ax1.set_facecolor('#bbffbb')
ax2 = fig.add_subplot(gs[1:4, 4:-6])
ax2.set_facecolor('#ffbbff')
ax3 = fig.add_subplot(gs[1:4, -6:-1])
ax3.set_facecolor('#00bbff')
# form
ax4, ax5, ax6, ax7 = [fig.add_subplot(gs[ 5+(i*6):10+(i*6), 1:10]) for i in range(4)]
[ax8, ax9, ax10, ax11] = [fig.add_subplot(gs[ 5+(i*6):10+(i*6), 11:20]) for i in range(4)]
axes = [ax4, ax8, ax5, ax9, ax6, ax10, ax7, ax11]
for ax in axes:
ax.set_facecolor('#bbbbbb')
for ax in fig.axes:
ax.set_xticks([])
ax.set_yticks([])
print(fig.properties()['figwidth'], fig.properties()['figheight'])
# from previous tests:
#fig.tight_layout(pad=0)
plot_form_page()
plt.savefig('page.tiff',
dpi=300,
orientation='portrait',
box_inches='tight', pad_inches=0)
plt.savefig('page.pdf',
dpi=300,
orientation='portrait',
bbox_inches='tight', pad_inches=0)
plt.savefig('page.eps',
dpi=300,
orientation='portrait',
papertype='a4',
bbox_inches='tight', pad_inches=0)
fig.properties() give me the right size for A4 in inches:
fig.properties()['figwidth']
fig.properties()['figheight']
8.26771653543307 11.69291338582677
But the figure shrinks to allow padding when it comes to printing
You can see the effects in the PDF file here:
https://www.dropbox.com/s/naqy9vj4ht6g2mv/Screenshot%202019-04-16%20at%2011.46.48.png?dl=0
With EPS is less of a problem:
https://www.dropbox.com/s/umycd8ofwod3nk5/Screenshot%202019-04-16%20at%2012.05.52.png?dl=0
(but again, I don't know how to create a multi-page EPS)
With the TIFF file I get the same:
https://www.dropbox.com/s/eid1zstbm954121/Screenshot%202019-04-16%20at%2012.11.59.png?dl=0
And this is not necessarily because the printing software adds the padding - the figure now is actually smaller, as it is 1929 × 2655 pixels at 300dpi, this makes it 6.43 × 8.85 in - certainly smaller than A4.
EDIT: This, of course, can be solved by printing with the option 'fit the page' but this could work with any image that has the proportions of an A4, I want to have control over the size, if that is possible.
Plot the data frame with 'o' and 'rx' style. To save the file in PDF format, use savefig() method where the image name is myImagePDF. pdf, format = ”pdf”. To show the image, use the plt.
Plot the second line using plot() method. Initialize a variable, filename, to make a pdf file. Create a user-defined function save_multi_image() to save multiple images in a PDF file. Call the save_multi_image() function with filename.
Saving a plot on your disk as an image file Now if you want to save matplotlib figures as image files programmatically, then all you need is matplotlib. pyplot. savefig() function. Simply pass the desired filename (and even location) and the figure will be stored on your disk.
Thanks to the comments of @ImportanceOfBeingErnest, and a bit of trial and error, I found a solution that creates an A4-shaped figure that when is saved as an A4 PDF page, it occupies the whole page (not 100% true, there is a very small margin, but it serves the purpose).
It just required to add gs.tight_layout(fig, pad=0)
. I do not fully understand why, but I suspect that when I was trying to eliminate the padding of the figure
object (with plt.savefig(..., bbox_inches='tight', pad_inches=0)
or fig.tight_layout(...)
), the figure might reach the border of the A4 page, but the GridSpec
would not, so the padding I was seeing was between the boundaries of the GridSpec
and the figure
, not with the border of the paper. When the padding between GridSpec
and figure
is set to 0, and the figure
has A4 size, things seem to work as I wanted.
(I still do not understand why in some cases the page of the PDF file would shrink in size but I am going to leave it here).
This is the code that does what I want, if you create the PDF file, open, and examine the properties, the size is A4, with the rectangles close to the border. WIth the other options, there was either a wide margin or a smaller page size.
import matplotlib.pyplot as plt
from matplotlib import gridspec
def plot_form_page():
# A4 canvas
fig_width_cm = 21 # A4 page
fig_height_cm = 29.7
inches_per_cm = 1 / 2.54 # Convert cm to inches
fig_width = fig_width_cm * inches_per_cm # width in inches
fig_height = fig_height_cm * inches_per_cm # height in inches
fig_size = [fig_width, fig_height]
plt.rc('text', usetex=False) # so that LaTeX is not needed when creating a PDF with PdfPages later on
fig = plt.figure()
fig.set_size_inches(fig_size)
fig.set_facecolor('#9999ff')
gs = gridspec.GridSpec(29, 21, wspace=0.1, hspace=0.1)
# external axis
ax0 = fig.add_subplot(gs[:, :])
# for side in ["top", "bottom", "left", "right"]:
# ax0.spines[side].set_visible(False)
# header
ax1 = fig.add_subplot(gs[1:4, 1:4])
ax1.set_facecolor('#bbffbb')
ax2 = fig.add_subplot(gs[1:4, 4:-6])
ax2.set_facecolor('#ffbbff')
ax3 = fig.add_subplot(gs[1:4, -6:-1])
ax3.set_facecolor('#00bbff')
# form
ax4, ax5, ax6, ax7 = [fig.add_subplot(gs[ 5+(i*6):10+(i*6), 1:10]) for i in range(4)]
[ax8, ax9, ax10, ax11] = [fig.add_subplot(gs[ 5+(i*6):10+(i*6), 11:20]) for i in range(4)]
axes = [ax4, ax8, ax5, ax9, ax6, ax10, ax7, ax11]
for ax in axes:
ax.set_facecolor('#bbbbbb')
for ax in fig.axes:
ax.set_xticks([])
ax.set_yticks([])
#print(fig.properties()['figwidth'], fig.properties()['figheight'])
gs.tight_layout(fig, pad=0)
plot_form_page()
plt.savefig('page.pdf',
dpi=300,
orientation='portrait')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With