test_backend_pdf.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import io
  2. import os
  3. from pathlib import Path
  4. import sys
  5. import tempfile
  6. import numpy as np
  7. import pytest
  8. from matplotlib import dviread, pyplot as plt, checkdep_usetex, rcParams
  9. from matplotlib.backends.backend_pdf import PdfPages
  10. from matplotlib.testing.compare import compare_images
  11. from matplotlib.testing.decorators import image_comparison
  12. needs_usetex = pytest.mark.skipif(
  13. not checkdep_usetex(True),
  14. reason="This test needs a TeX installation")
  15. @image_comparison(['pdf_use14corefonts.pdf'])
  16. def test_use14corefonts():
  17. rcParams['pdf.use14corefonts'] = True
  18. rcParams['font.family'] = 'sans-serif'
  19. rcParams['font.size'] = 8
  20. rcParams['font.sans-serif'] = ['Helvetica']
  21. rcParams['pdf.compression'] = 0
  22. text = '''A three-line text positioned just above a blue line
  23. and containing some French characters and the euro symbol:
  24. "Merci pépé pour les 10 €"'''
  25. fig = plt.figure()
  26. ax = fig.add_subplot(1, 1, 1)
  27. ax.set_title('Test PDF backend with option use14corefonts=True')
  28. ax.text(0.5, 0.5, text, horizontalalignment='center',
  29. verticalalignment='bottom',
  30. fontsize=14)
  31. ax.axhline(0.5, linewidth=0.5)
  32. def test_type42():
  33. rcParams['pdf.fonttype'] = 42
  34. fig = plt.figure()
  35. ax = fig.add_subplot(111)
  36. ax.plot([1, 2, 3])
  37. fig.savefig(io.BytesIO())
  38. def test_multipage_pagecount():
  39. with PdfPages(io.BytesIO()) as pdf:
  40. assert pdf.get_pagecount() == 0
  41. fig = plt.figure()
  42. ax = fig.add_subplot(111)
  43. ax.plot([1, 2, 3])
  44. fig.savefig(pdf, format="pdf")
  45. assert pdf.get_pagecount() == 1
  46. pdf.savefig()
  47. assert pdf.get_pagecount() == 2
  48. def test_multipage_properfinalize():
  49. pdfio = io.BytesIO()
  50. with PdfPages(pdfio) as pdf:
  51. for i in range(10):
  52. fig = plt.figure()
  53. ax = fig.add_subplot(111)
  54. ax.set_title('This is a long title')
  55. fig.savefig(pdf, format="pdf")
  56. pdfio.seek(0)
  57. assert sum(b'startxref' in line for line in pdfio) == 1
  58. assert sys.getsizeof(pdfio) < 40000
  59. def test_multipage_keep_empty():
  60. from matplotlib.backends.backend_pdf import PdfPages
  61. from tempfile import NamedTemporaryFile
  62. # test empty pdf files
  63. # test that an empty pdf is left behind with keep_empty=True (default)
  64. with NamedTemporaryFile(delete=False) as tmp:
  65. with PdfPages(tmp) as pdf:
  66. filename = pdf._file.fh.name
  67. assert os.path.exists(filename)
  68. os.remove(filename)
  69. # test if an empty pdf is deleting itself afterwards with keep_empty=False
  70. with PdfPages(filename, keep_empty=False) as pdf:
  71. pass
  72. assert not os.path.exists(filename)
  73. # test pdf files with content, they should never be deleted
  74. fig = plt.figure()
  75. ax = fig.add_subplot(111)
  76. ax.plot([1, 2, 3])
  77. # test that a non-empty pdf is left behind with keep_empty=True (default)
  78. with NamedTemporaryFile(delete=False) as tmp:
  79. with PdfPages(tmp) as pdf:
  80. filename = pdf._file.fh.name
  81. pdf.savefig()
  82. assert os.path.exists(filename)
  83. os.remove(filename)
  84. # test that a non-empty pdf is left behind with keep_empty=False
  85. with NamedTemporaryFile(delete=False) as tmp:
  86. with PdfPages(tmp, keep_empty=False) as pdf:
  87. filename = pdf._file.fh.name
  88. pdf.savefig()
  89. assert os.path.exists(filename)
  90. os.remove(filename)
  91. def test_composite_image():
  92. # Test that figures can be saved with and without combining multiple images
  93. # (on a single set of axes) into a single composite image.
  94. X, Y = np.meshgrid(np.arange(-5, 5, 1), np.arange(-5, 5, 1))
  95. Z = np.sin(Y ** 2)
  96. fig = plt.figure()
  97. ax = fig.add_subplot(1, 1, 1)
  98. ax.set_xlim(0, 3)
  99. ax.imshow(Z, extent=[0, 1, 0, 1])
  100. ax.imshow(Z[::-1], extent=[2, 3, 0, 1])
  101. plt.rcParams['image.composite_image'] = True
  102. with PdfPages(io.BytesIO()) as pdf:
  103. fig.savefig(pdf, format="pdf")
  104. assert len(pdf._file._images) == 1
  105. plt.rcParams['image.composite_image'] = False
  106. with PdfPages(io.BytesIO()) as pdf:
  107. fig.savefig(pdf, format="pdf")
  108. assert len(pdf._file._images) == 2
  109. def test_pdfpages_fspath():
  110. with PdfPages(Path(os.devnull)) as pdf:
  111. pdf.savefig(plt.figure())
  112. @image_comparison(['hatching_legend.pdf'])
  113. def test_hatching_legend():
  114. """Test for correct hatching on patches in legend"""
  115. fig = plt.figure(figsize=(1, 2))
  116. a = plt.Rectangle([0, 0], 0, 0, facecolor="green", hatch="XXXX")
  117. b = plt.Rectangle([0, 0], 0, 0, facecolor="blue", hatch="XXXX")
  118. fig.legend([a, b, a, b], ["", "", "", ""])
  119. @image_comparison(['grayscale_alpha.pdf'])
  120. def test_grayscale_alpha():
  121. """Masking images with NaN did not work for grayscale images"""
  122. x, y = np.ogrid[-2:2:.1, -2:2:.1]
  123. dd = np.exp(-(x**2 + y**2))
  124. dd[dd < .1] = np.nan
  125. fig, ax = plt.subplots()
  126. ax.imshow(dd, interpolation='none', cmap='gray_r')
  127. ax.set_xticks([])
  128. ax.set_yticks([])
  129. # This tests tends to hit a TeX cache lock on AppVeyor.
  130. @pytest.mark.flaky(reruns=3)
  131. @needs_usetex
  132. def test_missing_psfont(monkeypatch):
  133. """An error is raised if a TeX font lacks a Type-1 equivalent"""
  134. def psfont(*args, **kwargs):
  135. return dviread.PsFont(texname='texfont', psname='Some Font',
  136. effects=None, encoding=None, filename=None)
  137. monkeypatch.setattr(dviread.PsfontsMap, '__getitem__', psfont)
  138. rcParams['text.usetex'] = True
  139. fig, ax = plt.subplots()
  140. ax.text(0.5, 0.5, 'hello')
  141. with tempfile.TemporaryFile() as tmpfile, pytest.raises(ValueError):
  142. fig.savefig(tmpfile, format='pdf')
  143. @pytest.mark.style('default')
  144. def test_pdf_savefig_when_color_is_none(tmpdir):
  145. fig, ax = plt.subplots()
  146. plt.axis('off')
  147. ax.plot(np.sin(np.linspace(-5, 5, 100)), 'v', c='none')
  148. actual_image = tmpdir.join('figure.pdf')
  149. expected_image = tmpdir.join('figure.eps')
  150. fig.savefig(str(actual_image), format='pdf')
  151. fig.savefig(str(expected_image), format='eps')
  152. result = compare_images(str(actual_image), str(expected_image), 0)
  153. assert result is None
  154. @needs_usetex
  155. def test_failing_latex(tmpdir):
  156. """Test failing latex subprocess call"""
  157. path = str(tmpdir.join("tmpoutput.pdf"))
  158. rcParams['text.usetex'] = True
  159. # This fails with "Double subscript"
  160. plt.xlabel("$22_2_2$")
  161. with pytest.raises(RuntimeError):
  162. plt.savefig(path)
  163. def test_empty_rasterized():
  164. # Check that empty figures that are rasterised save to pdf files fine
  165. fig, ax = plt.subplots()
  166. ax.plot([], [], rasterized=True)
  167. fig.savefig(io.BytesIO(), format="pdf")