| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Hello all,
I seem to run into a problem when I use the (latex) package concmath, and try saving the resultant plot as a pdf (png and jpeg work fine). As suggested in #10272, I've tried installing the "cm-super" package, but this does not solve the issue. I've also played around with the backends, but the problem still persists. A short code that generates this issue:
fig = plt.figure(figsize=(4, 4))
ax = fig.add_axes([0, 0, 1, 1])
plt.rcParams['font.family'] = 'monospace'
plt.rcParams['text.usetex'] = True
rc('text.latex', preamble=r'\usepackage[OT1]{fontenc}\usepackage{concmath}')
plt.plot([10**1,10**9], [1,10], label='Test M$_\star$')
plt.scatter([10**1,10**9], [1,10], label='Test1 M$_\star$ = 10M$_\odot$')
plt.xscale('log')
plt.yscale('log')
plt.xlabel('Test')
plt.ylabel('Test')
plt.xlim(10, 10**9)
plt.legend()
plt.savefig('test.pdf', bbox_inches='tight')
with the following traceback:
KeyError Traceback (most recent call last)
<ipython-input-3-de7a1b0d622a> in <module>
14 plt.legend()
15
---> 16 plt.savefig('test_sm.pdf', bbox_inches='tight')
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/pyplot.py in savefig(*args, **kwargs)
964 def savefig(*args, **kwargs):
965 fig = gcf()
--> 966 res = fig.savefig(*args, **kwargs)
967 fig.canvas.draw_idle() # need this if 'transparent=True' to reset colors
968 return res
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/figure.py in savefig(self, fname, transparent, **kwargs)
3003 patch.set_edgecolor('none')
3004
-> 3005 self.canvas.print_figure(fname, **kwargs)
3006
3007 if transparent:
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
2253 # force the figure dpi to 72), so we need to set it again here.
2254 with cbook._setattr_cm(self.figure, dpi=dpi):
-> 2255 result = print_method(
2256 filename,
2257 facecolor=facecolor,
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backend_bases.py in wrapper(*args, **kwargs)
1667 kwargs.pop(arg)
1668
-> 1669 return func(*args, **kwargs)
1670
1671 return wrapper
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/_api/deprecation.py in wrapper(*inner_args, **inner_kwargs)
429 else deprecation_addendum,
430 **kwargs)
--> 431 return func(*inner_args, **inner_kwargs)
432
433 return wrapper
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backends/backend_pdf.py in print_pdf(self, filename, dpi, bbox_inches_restore, metadata)
2723 RendererPdf(file, dpi, height, width),
2724 bbox_inches_restore=bbox_inches_restore)
-> 2725 self.figure.draw(renderer)
2726 renderer.finalize()
2727 if not isinstance(filename, PdfPages):
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
72 @wraps(draw)
73 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 74 result = draw(artist, renderer, *args, **kwargs)
75 if renderer._rasterizing:
76 renderer.stop_rasterizing()
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
49 renderer.start_filter()
50
---> 51 return draw(artist, renderer, *args, **kwargs)
52 finally:
53 if artist.get_agg_filter() is not None:
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/figure.py in draw(self, renderer)
2778
2779 self.patch.draw(renderer)
-> 2780 mimage._draw_list_compositing_images(
2781 renderer, self, artists, self.suppressComposite)
2782
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
49 renderer.start_filter()
50
---> 51 return draw(artist, renderer, *args, **kwargs)
52 finally:
53 if artist.get_agg_filter() is not None:
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/_api/deprecation.py in wrapper(*inner_args, **inner_kwargs)
429 else deprecation_addendum,
430 **kwargs)
--> 431 return func(*inner_args, **inner_kwargs)
432
433 return wrapper
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axes/_base.py in draw(self, renderer, inframe)
2919 renderer.stop_rasterizing()
2920
-> 2921 mimage._draw_list_compositing_images(renderer, self, artists)
2922
2923 renderer.close_group('axes')
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
49 renderer.start_filter()
50
---> 51 return draw(artist, renderer, *args, **kwargs)
52 finally:
53 if artist.get_agg_filter() is not None:
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axis.py in draw(self, renderer, *args, **kwargs)
1139
1140 for tick in ticks_to_draw:
-> 1141 tick.draw(renderer)
1142
1143 # scale up the axis label box to also find the neighbors, not
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
49 renderer.start_filter()
50
---> 51 return draw(artist, renderer, *args, **kwargs)
52 finally:
53 if artist.get_agg_filter() is not None:
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axis.py in draw(self, renderer)
300 for artist in [self.gridline, self.tick1line, self.tick2line,
301 self.label1, self.label2]:
--> 302 artist.draw(renderer)
303 renderer.close_group(self.__name__)
304 self.stale = False
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
49 renderer.start_filter()
50
---> 51 return draw(artist, renderer, *args, **kwargs)
52 finally:
53 if artist.get_agg_filter() is not None:
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/text.py in draw(self, renderer)
721
722 if textobj.get_usetex():
--> 723 textrenderer.draw_tex(gc, x, y, clean_line,
724 textobj._fontproperties, angle,
725 mtext=mtext)
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/_api/deprecation.py in wrapper(*inner_args, **inner_kwargs)
429 else deprecation_addendum,
430 **kwargs)
--> 431 return func(*inner_args, **inner_kwargs)
432
433 return wrapper
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backends/backend_pdf.py in draw_tex(self, gc, x, y, s, prop, angle, ismath, mtext)
2190 for x1, y1, dvifont, glyph, width in page.text:
2191 if dvifont != oldfont:
-> 2192 pdfname = self.file.dviFontName(dvifont)
2193 seq += [['font', pdfname, dvifont.size]]
2194 oldfont = dvifont
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backends/backend_pdf.py in dviFontName(self, dvifont)
863
864 tex_font_map = dviread.PsfontsMap(dviread.find_tex_file('pdftex.map'))
--> 865 psfont = tex_font_map[dvifont.texname]
866 if psfont.filename is None:
867 raise ValueError(
~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/dviread.py in __getitem__(self, texname)
856 assert isinstance(texname, bytes)
857 try:
--> 858 result = self._font[texname]
859 except KeyError:
860 fmt = ('A PostScript file for the font whose TeX name is "{0}" '
KeyError: b'ccr10'
Please let me know if there's a way to circumvent this issue. Thank you all in advance!
Operating system: macOS Big Sur 11.2.3
Matplotlib version: 3.4.2
Matplotlib backend: module://ipykernel.pylab.backend_inline
Python version: 3.8.8
Matploblib came pre-installed with anaconda, but upgraded through pip.
| Back | FazBrowse Home | New Git URL |
Activity
anntzer commented on Jun 19, 2021
From some research, it looks like this arises because concmath uses fonts in the Metafont format, which is not supported by Matplotlib. If you run either latex+dvips or pdflatex on a single tex file that uses such fonts, you'll see in the log an invocation of mktexpk and mf-nowin to convert the Metafont font to a bitmap (pk) format (you may need to clear your user-level texlive cache to see that, though). So we'd need to either look into supporting pk fonts as well (likely this will run into FreeType's lack of support, see the last entry at https://www.freetype.org/gsoc.html), or into auto-conversion of Metafont to e.g. Type1 (the usual font supported for usetex), which is not trivial (https://en.wikipedia.org/wiki/Metafont#Producing_PostScript_Type_1_fonts).
(I'm tempted to close as "upstream fix required" due to the situation with FreeType.)
oorc06 commented on Jun 19, 2021
Sorry if this is a naive question, but I'm confused about this statement: "..., which is not supported by Matplotlib.": Everything works fine as long as one doesn't try saving the plot as a pdf. For example, as mentioned earlier, there are no problems if one tries to save the figure as a png or jpeg file (and there are, of course, no issues encountered if one doesn't try to save the file at all). So wouldn't this mean that Matplotlib does support the font, and rather the problem is with saving into a pdf?
anntzer commented on Jun 19, 2021
No, it's not a naive question at all!
That's because for png/jpeg output, we rely on dvipng to rasterize the text (i.e. transform the text into an image). For pdf we don't do that, as the point of pdf is normally to have selectable text, but then we need to actually know how to embed the font and the glyph infos into the pdf file (which is what's not supported here). I guess yet another solution would be to give up on embedding the font/glyphs in that case and just embed a dvipng-generated image...
@aitikgupta I guess this is actually relevant for your gsoc: switching to parsing dvi even for raster output would mean a small loss of functionality (for agg) in this case, and conversely, perhaps falling back on embedding dvipng rasters would be a relatively simple workaround for vector output?
aitikgupta commented on Jun 20, 2021
hmm, what if instead we let users decide this?
A parameter while exporting a PDF: editable (=True by default), but if the user decides that no, they don't want editable PDFs, they could switch the flag (and we will end up using the dvipng route to just embed the text image in the PDF).
(edit: This could be a much simpler way of getting around user-end issues, such as this one)
On a different note, non-editable PDFs hardly make sense? If users want to use just 'images' in their PDFs (and not actual text), they could just convert PNG outputs. (or we could provide a flag like mentioned above)
But as a library, I think we should stick to editable PDFs only, and possibly fix the issue of PK fonts.
anntzer commented on Jun 20, 2021
Just a side point: I realized we don't actually need to figure out the correct mktexpk/mf invocations ourselves, at least that part can be handled transparently by kpathsea (https://tug.org/texinfohtml/kpathsea.html#mktex-scripts). (We need to call something like kpsewhich -mktex pk ccr10.600pk (where 600 should be replaced by some dpi-like value).)
Making things user-configurable seems fine, although as always that means that both branches need to be implemented :-)
After some more investigation, it looks like pdflatex embeds such fonts as type3, taking advantage of the possibility to just specify glyphs as raster images (after all, it can use arbitrary postscript operators to define glyphs). (We'd still need to somehow get the bitmap out of the pk file, though.)
anntzer commented on Sep 11, 2021
Just for future reference, here's a minimal repro:
Install the concmath and concrete ctan packages, then
anntzer commented on Sep 19, 2024
I guess if we want to work on this, the first part would be to implement a parser for the pk format, following the description at https://ctan.math.illinois.edu/info/knuth-pdf/mfware/gftopk.pdf (starting at section 21). Probably a good programming exercise ;-)