diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 5dbd1bcb2f19..445f13380897 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -262,7 +262,7 @@ def _get_agg_font(self, prop): font = RendererAgg._fontd.get(fname) if font is None: font = FT2Font( - str(fname), + fname, hinting_factor=rcParams['text.hinting_factor']) RendererAgg._fontd[fname] = font RendererAgg._fontd[key] = font diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 9a77090c7d48..308594e98db4 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -585,7 +585,7 @@ def fontName(self, fontprop): self.fontNames[filename] = Fx self.nextFont += 1 matplotlib.verbose.report( - 'Assigning font %s = %s' % (Fx, filename), + 'Assigning font %s = %r' % (Fx, filename), 'debug') return Fx @@ -701,7 +701,7 @@ def createType1Descriptor(self, t1font, fontfile): if 0: flags |= 1 << 17 # TODO: small caps if 0: flags |= 1 << 18 # TODO: force bold - ft2font = FT2Font(str(fontfile)) + ft2font = FT2Font(fontfile) descriptor = { 'Type': Name('FontDescriptor'), @@ -761,7 +761,7 @@ def _get_xobject_symbol_name(self, filename, symbol_name): def embedTTF(self, filename, characters): """Embed the TTF font from the named file into the document.""" - font = FT2Font(str(filename)) + font = FT2Font(filename) fonttype = rcParams['pdf.fonttype'] def cvt(length, upe=font.units_per_EM, nearest=True): @@ -845,7 +845,8 @@ def get_char_width(charcode): # Make the charprocs array (using ttconv to generate the # actual outlines) - rawcharprocs = ttconv.get_pdf_charprocs(filename, glyph_ids) + rawcharprocs = ttconv.get_pdf_charprocs( + filename.encode(sys.getfilesystemencoding()), glyph_ids) charprocs = {} charprocsRef = {} for charname, stream in six.iteritems(rawcharprocs): @@ -2003,7 +2004,7 @@ def _get_font_ttf(self, prop): filename = findfont(prop) font = self.truetype_font_cache.get(filename) if font is None: - font = FT2Font(str(filename)) + font = FT2Font(filename) self.truetype_font_cache[filename] = font self.truetype_font_cache[key] = font font.clear() diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index b2125c354123..70ea29a0c1bb 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -33,7 +33,7 @@ system_fonts = [] for f in font_manager.findSystemFonts(): try: - system_fonts.append(FT2Font(str(f)).family_name) + system_fonts.append(FT2Font(f).family_name) except RuntimeError: pass # some fonts on osx are known to fail, print? except: diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 09791df594a0..17eb98ce689e 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -391,7 +391,7 @@ def _get_font_ttf(self, prop): fname = findfont(prop) font = self.fontd.get(fname) if font is None: - font = FT2Font(str(fname)) + font = FT2Font(fname) self.fontd[fname] = font self.fontd[key] = font font.clear() @@ -1131,7 +1131,7 @@ def print_figure_impl(): if not rcParams['ps.useafm']: for font_filename, chars in six.itervalues(ps_renderer.used_characters): if len(chars): - font = FT2Font(str(font_filename)) + font = FT2Font(font_filename) cmap = font.get_charmap() glyph_ids = [] for c in chars: @@ -1153,7 +1153,9 @@ def print_figure_impl(): raise RuntimeError("OpenType CFF fonts can not be saved using the internal Postscript backend at this time.\nConsider using the Cairo backend.") else: fh.flush() - convert_ttf_to_ps(font_filename, raw_fh, fonttype, glyph_ids) + convert_ttf_to_ps( + font_filename.encode(sys.getfilesystemencoding()), + raw_fh, fonttype, glyph_ids) print("end", file=fh) print("%%EndProlog", file=fh) diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index dab2d38b4faf..5930dfcb3380 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -323,7 +323,7 @@ def _get_font(self, prop): fname = findfont(prop) font = self.fontd.get(fname) if font is None: - font = FT2Font(str(fname)) + font = FT2Font(fname) self.fontd[fname] = font self.fontd[key] = font font.clear() diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index aae654d36802..3b8213582124 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -62,7 +62,6 @@ parse_fontconfig_pattern, generate_fontconfig_pattern USE_FONTCONFIG = False - verbose = matplotlib.verbose font_scalings = { @@ -269,16 +268,20 @@ def get_fontconfig_fonts(fontext='ttf'): fontfiles = {} try: - pipe = subprocess.Popen(['fc-list', '', 'file'], stdout=subprocess.PIPE) + pipe = subprocess.Popen(['fc-list', '--format=%{file}\\n'], stdout=subprocess.PIPE) output = pipe.communicate()[0] except (OSError, IOError): # Calling fc-list did not work, so we'll just return nothing return fontfiles if pipe.returncode == 0: - output = str(output) - for line in output.split('\n'): - fname = line.split(':')[0] + # The line breaks between results are in ascii, but each entry + # is in in sys.filesystemencoding(). + for fname in output.split(b'\n'): + try: + fname = six.text_type(fname, sys.getfilesystemencoding()) + except UnicodeDecodeError: + continue if (os.path.splitext(fname)[1][1:] in fontext and os.path.exists(fname)): fontfiles[fname] = 1 @@ -570,7 +573,7 @@ def createFontList(fontfiles, fontext='ttf'): continue else: try: - font = ft2font.FT2Font(str(fpath)) + font = ft2font.FT2Font(fpath) except RuntimeError: verbose.report("Could not open font file %s"%fpath) continue @@ -720,7 +723,7 @@ def get_name(self): Return the name of the font that best matches the font properties. """ - return ft2font.FT2Font(str(findfont(self))).family_name + return ft2font.FT2Font(findfont(self)).family_name def get_style(self): """ @@ -1246,7 +1249,7 @@ def findfont(self, prop, fontext='ttf', directory=None, else: verbose.report( 'findfont: Matching %s to %s (%s) with score of %f' % - (prop, best_font.name, best_font.fname, best_score)) + (prop, best_font.name, repr(best_font.fname), best_score)) result = best_font.fname if not os.path.isfile(result): @@ -1292,19 +1295,26 @@ def fc_match(pattern, fontext): fontexts = get_fontext_synonyms(fontext) ext = "." + fontext try: - pipe = subprocess.Popen(['fc-match', '-sv', pattern], stdout=subprocess.PIPE) + pipe = subprocess.Popen( + ['fc-match', '-s', '--format=%{file}\\n', pattern], + stdout=subprocess.PIPE) output = pipe.communicate()[0] except (OSError, IOError): return None + + # The bulk of the output from fc-list is ascii, so we keep the + # result in bytes and parse it as bytes, until we extract the + # filename, which is in sys.filesystemencoding(). if pipe.returncode == 0: - for match in _fc_match_regex.finditer(output): - file = match.group(1) - file = file.decode(sys.getfilesystemencoding()) - if os.path.splitext(file)[1][1:] in fontexts: - return file + for fname in output.split(b'\n'): + try: + fname = six.text_type(fname, sys.getfilesystemencoding()) + except UnicodeDecodeError: + continue + if os.path.splitext(fname)[1][1:] in fontexts: + return fname return None - _fc_match_regex = re.compile(br'\sfile:\s+"([^"]*)"') _fc_match_cache = {} def findfont(prop, fontext='ttf'): diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py index 445a91bc1c48..98904707567d 100644 --- a/lib/matplotlib/mathtext.py +++ b/lib/matplotlib/mathtext.py @@ -566,7 +566,7 @@ def __init__(self, default_font_prop, mathtext_backend): self._fonts = {} filename = findfont(default_font_prop) - default_font = self.CachedFont(FT2Font(str(filename))) + default_font = self.CachedFont(FT2Font(filename)) self._fonts['default'] = default_font self._fonts['regular'] = default_font @@ -581,8 +581,8 @@ def _get_font(self, font): basename = font cached_font = self._fonts.get(basename) - if cached_font is None: - font = FT2Font(str(basename)) + if cached_font is None and os.path.exists(basename): + font = FT2Font(basename) cached_font = self.CachedFont(font) self._fonts[basename] = cached_font self._fonts[font.postscript_name] = cached_font @@ -697,20 +697,14 @@ def _get_glyph(self, fontname, font_class, sym, fontsize): if fontname in self.fontmap and sym in latex_to_bakoma: basename, num = latex_to_bakoma[sym] slanted = (basename == "cmmi10") or sym in self._slanted_symbols - try: - cached_font = self._get_font(basename) - except RuntimeError: - pass - else: + cached_font = self._get_font(basename) + if cached_font is not None: symbol_name = cached_font.font.get_glyph_name(num) num = cached_font.glyphmap[num] elif len(sym) == 1: slanted = (fontname == "it") - try: - cached_font = self._get_font(fontname) - except RuntimeError: - pass - else: + cached_font = self._get_font(fontname) + if cached_font is not None: num = ord(sym) gid = cached_font.charmap.get(num) if gid is not None: @@ -852,11 +846,8 @@ def _get_glyph(self, fontname, font_class, sym, fontsize): slanted = (new_fontname == 'it') or sym in self._slanted_symbols found_symbol = False - try: - cached_font = self._get_font(new_fontname) - except RuntimeError: - pass - else: + cached_font = self._get_font(new_fontname) + if cached_font is not None: try: glyphindex = cached_font.charmap[uniindex] found_symbol = True diff --git a/lib/matplotlib/textpath.py b/lib/matplotlib/textpath.py index db837898dbdd..8def00b7b038 100644 --- a/lib/matplotlib/textpath.py +++ b/lib/matplotlib/textpath.py @@ -56,7 +56,7 @@ def _get_font(self, prop): find a ttf font. """ fname = font_manager.findfont(prop) - font = FT2Font(str(fname)) + font = FT2Font(fname) font.set_size(self.FONT_SCALE, self.DPI) return font @@ -338,7 +338,7 @@ def get_glyphs_tex(self, prop, s, glyph_map=None, font_bunch = self.tex_font_map[dvifont.texname] if font_and_encoding is None: - font = FT2Font(str(font_bunch.filename)) + font = FT2Font(font_bunch.filename) for charmap_name, charmap_code in [("ADOBE_CUSTOM", 1094992451), diff --git a/src/_macosx.m b/src/_macosx.m index 03480645c80a..c821354dc008 100644 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -1339,11 +1339,11 @@ static int _transformation_converter(PyObject* object, void* pointer) return NULL; } const Py_ssize_t Npaths = PySequence_Size(path_ids); - + /* -------------------------------------------------------------------- */ CGContextSaveGState(cr); - + /* ------------------- Check facecolors array ------------------------- */ facecolors = PyArray_FromObject(facecolors, NPY_DOUBLE, 1, 2); @@ -1948,7 +1948,7 @@ static int _transformation_converter(PyObject* object, void* pointer) const double a = *(double*)PyArray_GETPTR2(edgecolors, fi, 3); CGContextSetRGBStrokeColor(cr, r, g, b, a); } - + if (Nfacecolors > 0) { if (Nedgecolors > 0 || antialiased) @@ -2343,9 +2343,7 @@ static CGRect _find_enclosing_rect(CGPoint points[3]) #else ATSFontRef font = 0; #endif -#if PY3K PyObject* ascii = NULL; -#endif const int k = (strcmp(italic, "italic") ? 0 : 2) + (strcmp(weight, "bold") ? 0 : 1); @@ -2526,14 +2524,9 @@ static CGRect _find_enclosing_rect(CGPoint points[3]) for (i = 0; i < n; i++) { PyObject* item = PyList_GET_ITEM(family, i); -#if PY3K ascii = PyUnicode_AsASCIIString(item); if(!ascii) return 0; temp = PyBytes_AS_STRING(ascii); -#else - if(!PyString_Check(item)) return 0; - temp = PyString_AS_STRING(item); -#endif for (j = 0; j < NMAP; j++) { if (!strcmp(map[j].name, temp)) { temp = psnames[map[j].index][k]; @@ -2560,10 +2553,8 @@ static CGRect _find_enclosing_rect(CGPoint points[3]) name = temp; break; } -#if PY3K Py_DECREF(ascii); ascii = NULL; -#endif } if(!font) { string = CFStringCreateWithCString(kCFAllocatorDefault, @@ -2576,8 +2567,14 @@ static CGRect _find_enclosing_rect(CGPoint points[3]) #endif CFRelease(string); } + if (font == NULL) + { + PyErr_SetString(PyExc_ValueError, "Could not load font"); + } #ifndef COMPILING_FOR_10_5 - CGContextSelectFont(cr, name, size, kCGEncodingMacRoman); + else { + CGContextSelectFont(cr, name, size, kCGEncodingMacRoman); + } #endif #if PY3K Py_XDECREF(ascii); @@ -2641,7 +2638,11 @@ static CGRect _find_enclosing_rect(CGPoint points[3]) CFStringRef s = CFStringCreateWithCharacters(kCFAllocatorDefault, text, n); #endif - font = setfont(cr, family, size, weight, italic); + if (!(font = setfont(cr, family, size, weight, italic))) + { + CFRelease(s); + return NULL; + } color = CGColorCreateGenericRGB(self->color[0], self->color[1], @@ -2748,7 +2749,11 @@ static CGRect _find_enclosing_rect(CGPoint points[3]) CFStringRef s = CFStringCreateWithCharacters(kCFAllocatorDefault, text, n); #endif - font = setfont(cr, family, size, weight, italic); + if (!(font = setfont(cr, family, size, weight, italic))) + { + CFRelease(s); + return NULL; + }; CFStringRef keys[1]; CFTypeRef values[1]; @@ -2819,7 +2824,10 @@ static CGRect _find_enclosing_rect(CGPoint points[3]) &italic, &angle)) return NULL; - atsfont = setfont(cr, family, size, weight, italic); + if (!(atsfont = setfont(cr, family, size, weight, italic)) + { + return NULL; + } OSStatus status; @@ -2904,7 +2912,10 @@ static CGRect _find_enclosing_rect(CGPoint points[3]) if(!PyArg_ParseTuple(args, "u#Ofss", &text, &n, &family, &size, &weight, &italic)) return NULL; - atsfont = setfont(cr, family, size, weight, italic); + if (!(atsfont = setfont(cr, family, size, weight, italic))) + { + return NULL; + } OSStatus status = noErr; ATSUAttributeTag tags[] = {kATSUFontTag, @@ -5681,7 +5692,7 @@ - (void)scrollWheel:(NSEvent*)event } - (BOOL)acceptsFirstResponder -{ +{ return YES; } diff --git a/src/_ttconv.cpp b/src/_ttconv.cpp index 8fb1822c69fa..5b3e1d801df9 100644 --- a/src/_ttconv.cpp +++ b/src/_ttconv.cpp @@ -124,7 +124,11 @@ convert_ttf_to_ps(PyObject* self, PyObject* args, PyObject* kwds) }; if (! PyArg_ParseTupleAndKeywords (args, kwds, + #if PY_MAJOR_VERSION == 3 + "yO&i|O&:convert_ttf_to_ps", + #else "sO&i|O&:convert_ttf_to_ps", + #endif (char**)kwlist, &filename, fileobject_to_PythonFileWriter, @@ -202,7 +206,11 @@ py_get_pdf_charprocs(PyObject* self, PyObject* args, PyObject* kwds) static const char *kwlist[] = { "filename", "glyph_ids", NULL }; if (! PyArg_ParseTupleAndKeywords (args, kwds, + #if PY_MAJOR_VERSION == 3 + "y|O&:get_pdf_charprocs", + #else "s|O&:get_pdf_charprocs", + #endif (char **)kwlist, &filename, pyiterable_to_vector_int, diff --git a/src/ft2font.cpp b/src/ft2font.cpp index a454a8e3f0a8..0fb3b8b65825 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -850,7 +850,9 @@ FT2Font::FT2Font(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwds) { FT_Open_Args open_args; - std::string facefile = Py::String(args[0]).encode("utf-8"); + /* This string is only used for error messages, so encode it in something + * that we'll always be able to print. */ + std::string facefile = Py::String(args[0]).encode("unicode_escape"); args.verify_length(1); @@ -861,9 +863,8 @@ FT2Font::FT2Font(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwds) mem_size = 0; if (make_open_args(args[0].ptr(), &open_args)) { - std::ostringstream s; - s << "Could not load facefile " << facefile << "; Unknown_File_Format" << std::endl; - throw Py::RuntimeError(s.str()); + /* make_open_args sets the Python exception for us. */ + throw Py::Exception(); } int error = FT_Open_Face(_ft2Library, &open_args, 0, &face); @@ -965,7 +966,7 @@ FT2Font::FT2Font(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwds) setattro("underline_thickness", Py::Int(face->underline_thickness)); } - setattro("fname", Py::String(facefile)); + setattro("fname", args[0]); _VERBOSE("FT2Font::FT2Font done"); } @@ -2092,9 +2093,8 @@ FT2Font::attach_file(const Py::Tuple &args) if (make_open_args(args[0].ptr(), &open_args)) { - std::ostringstream s; - s << "Could not attach file " << filename << std::endl; - throw Py::RuntimeError(s.str()); + /* make_open_args sets the Python exception for us. */ + throw Py::Exception(); } FT_Error error = FT_Attach_Stream(face, &open_args);