From 8b2257dbff27e9c66a4c04fdbdc0b77949695826 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Mon, 26 Jul 2021 03:41:52 +0530 Subject: [PATCH 01/32] Parse fallback_list through wrapper --- src/ft2font_wrapper.cpp | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 06cbc22dad9c..634f1dd52986 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -264,6 +264,7 @@ typedef struct Py_ssize_t shape[2]; Py_ssize_t strides[2]; Py_ssize_t suboffsets[2]; + std::vector fallbacks; } PyFT2Font; static unsigned long read_from_file_callback(FT_Stream stream, @@ -361,15 +362,18 @@ const char *PyFT2Font_init__doc__ = static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds) { - PyObject *filename = NULL, *open = NULL, *data = NULL; + printf("PyFT2Font init!\n"); + PyObject *filename = NULL, *open = NULL, *data = NULL, *fallback_list = NULL; FT_Open_Args open_args; long hinting_factor = 8; int kerning_factor = 0; - const char *names[] = { "filename", "hinting_factor", "_kerning_factor", NULL }; + const char *names[] = { + "filename", "hinting_factor", "_fallback_list", "_kerning_factor", NULL + }; if (!PyArg_ParseTupleAndKeywords( - args, kwds, "O|l$i:FT2Font", (char **)names, &filename, - &hinting_factor, &kerning_factor)) { + args, kwds, "O|l$Oi:FT2Font", (char **)names, &filename, + &hinting_factor, &fallback_list, &kerning_factor)) { return -1; } @@ -382,6 +386,21 @@ static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds) open_args.flags = FT_OPEN_STREAM; open_args.stream = &self->stream; + if (fallback_list && PyList_Check(fallback_list)) { + Py_ssize_t size = PyList_Size(fallback_list); + + for (int i = 0; i < size; ++i) { + PyObject* item = PyList_GetItem(fallback_list, i); + + // TODO: check whether item is actually an FT2Font + FT2Font *fback = reinterpret_cast(item)->x; + self->fallbacks.push_back(fback); + } + + Py_INCREF(fallback_list); + } + printf("Fallback SIZE: %lu \n", self->fallbacks.size()); + if (PyBytes_Check(filename) || PyUnicode_Check(filename)) { if (!(open = PyDict_GetItemString(PyEval_GetBuiltins(), "open")) // Borrowed reference. || !(self->py_file = PyObject_CallFunction(open, "Os", filename, "rb"))) { @@ -403,7 +422,7 @@ static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds) Py_CLEAR(data); CALL_CPP_FULL( - "FT2Font", (self->x = new FT2Font(open_args, hinting_factor)), + "FT2Font", (self->x = new FT2Font(open_args, hinting_factor, self->fallbacks)), Py_CLEAR(self->py_file), -1); CALL_CPP_INIT("FT2Font->set_kerning_factor", (self->x->set_kerning_factor(kerning_factor))); @@ -444,6 +463,7 @@ static PyObject *PyFT2Font_set_size(PyFT2Font *self, PyObject *args, PyObject *k { double ptsize; double dpi; + printf("PyFT, set_size called!\n"); if (!PyArg_ParseTuple(args, "dd:set_size", &ptsize, &dpi)) { return NULL; From 8114a54eaf5de3e954684c7831e0a9cfc14376ec Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Mon, 26 Jul 2021 03:42:58 +0530 Subject: [PATCH 02/32] Define new/modify previous FT2Font functions --- src/ft2font.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ft2font.h b/src/ft2font.h index 0863f3450b36..b09ad801bd34 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -69,17 +69,27 @@ class FT2Font { public: - FT2Font(FT_Open_Args &open_args, long hinting_factor); + FT2Font(FT_Open_Args &open_args, long hinting_factor, std::vector &fallback_list); virtual ~FT2Font(); void clear(); void set_size(double ptsize, double dpi); void set_charmap(int i); + void set_fallbacks(std::vector &fallback_list); + void check(); void select_charmap(unsigned long i); void set_text( size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector &xys); int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode); + int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &delta); void set_kerning_factor(int factor); void load_char(long charcode, FT_Int32 flags); + void load_char_with_fallback(FT2Font* &ft_object_with_glyph, + FT_UInt &final_glyph_index, + std::vector &parent_glyphs, + long charcode, + FT_Int32 flags, + FT_Error &charcode_error, + FT_Error &glyph_error); void load_glyph(FT_UInt glyph_index, FT_Int32 flags); void get_width_height(long *width, long *height); void get_bitmap_offset(long *x, long *y); @@ -123,6 +133,7 @@ class FT2Font FT_Face face; FT_Vector pen; /* untransformed origin */ std::vector glyphs; + std::vector fallbacks; FT_BBox bbox; FT_Pos advance; long hinting_factor; From a4b78c8554b4464a4e82a894ac33afd297a4c2c1 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Mon, 26 Jul 2021 03:44:05 +0530 Subject: [PATCH 03/32] Implement new/modify previous FT2Font functions --- src/ft2font.cpp | 150 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 133 insertions(+), 17 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index fdea4c39bec0..0b52c72551db 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -169,12 +169,8 @@ FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1, m_dirty = true; } -static FT_UInt ft_get_char_index_or_warn(FT_Face face, FT_ULong charcode) +static FT_UInt ft_glyph_warn(FT_ULong charcode) { - FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); - if (glyph_index) { - return glyph_index; - } PyObject *text_helpers = NULL, *tmp = NULL; if (!(text_helpers = PyImport_ImportModule("matplotlib._text_helpers")) || !(tmp = PyObject_CallMethod(text_helpers, "warn_on_missing_glyph", "k", charcode))) { @@ -189,6 +185,14 @@ static FT_UInt ft_get_char_index_or_warn(FT_Face face, FT_ULong charcode) return 0; } +static FT_UInt ft_get_char_index_or_warn(FT_Face face, FT_ULong charcode) +{ + FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); + if (glyph_index) { + return glyph_index; + } + return ft_glyph_warn(charcode); +} // ft_outline_decomposer should be passed to FT_Outline_Decompose. On the // first pass, vertices and codes are set to NULL, and index is simply @@ -319,7 +323,10 @@ FT2Font::get_path() return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj()); } -FT2Font::FT2Font(FT_Open_Args &open_args, long hinting_factor_) : image(), face(NULL) +FT2Font::FT2Font(FT_Open_Args &open_args, + long hinting_factor_, + std::vector &fallback_list) + : image(), face(NULL) { clear(); @@ -353,6 +360,9 @@ FT2Font::FT2Font(FT_Open_Args &open_args, long hinting_factor_) : image(), face( FT_Matrix transform = { 65536 / hinting_factor, 0, 0, 65536 }; FT_Set_Transform(face, &transform, 0); + + // Set fallbacks + set_fallbacks(fallback_list); } FT2Font::~FT2Font() @@ -364,6 +374,10 @@ FT2Font::~FT2Font() if (face) { FT_Done_Face(face); } + + for (uint i = 0; i < fallbacks.size(); i ++) { + fallbacks[i]->~FT2Font(); + } } void FT2Font::clear() @@ -376,6 +390,17 @@ void FT2Font::clear() } glyphs.clear(); + + for (uint i = 0; i < fallbacks.size(); i ++) { + fallbacks[i]->clear(); + } +} + +void FT2Font::check() { + printf("Fallback num: -1; Numface: %lu\n", get_face()->num_glyphs); + for (uint i = 0; i < fallbacks.size(); i++) { + printf("Fallback num: %u; Numface: %lu\n", i, fallbacks[i]->get_face()->num_glyphs); + } } void FT2Font::set_size(double ptsize, double dpi) @@ -387,8 +412,18 @@ void FT2Font::set_size(double ptsize, double dpi) } FT_Matrix transform = { 65536 / hinting_factor, 0, 0, 65536 }; FT_Set_Transform(face, &transform, 0); + + for (uint i = 0; i < fallbacks.size(); i++) { + fallbacks[i]->set_size(ptsize, dpi); + } } +void FT2Font::set_fallbacks(std::vector &fallback_list) +{ + fallbacks.assign(fallback_list.begin(), fallback_list.end()); +} + + void FT2Font::set_charmap(int i) { if (i >= face->num_charmaps) { @@ -421,15 +456,32 @@ int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode) } } +int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &delta) +{ + if (!FT_HAS_KERNING(face)) { + return 0; + } + + if (!FT_Get_Kerning(face, left, right, mode, &delta)) { + return (int)(delta.x) / (hinting_factor << kerning_factor); + } else { + return 0; + } +} + void FT2Font::set_kerning_factor(int factor) { kerning_factor = factor; + for (uint i = 0; i < fallbacks.size(); i ++){ + fallbacks[i]->set_kerning_factor(factor); + } } void FT2Font::set_text( size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector &xys) { FT_Matrix matrix; /* transformation matrix */ + check(); angle = angle / 360.0 * 2 * M_PI; @@ -452,28 +504,47 @@ void FT2Font::set_text( FT_BBox glyph_bbox; FT_Pos last_advance; - glyph_index = ft_get_char_index_or_warn(face, codepoints[n]); + FT_UInt final_glyph_index; + FT_Error charcode_error, glyph_error; + FT2Font *ft_object_with_glyph = this; + printf("\nBefore loading char! \n"); + load_char_with_fallback(ft_object_with_glyph, final_glyph_index, glyphs, codepoints[n], + flags, charcode_error, glyph_error); + printf("Final ft2font: %lu\n", ft_object_with_glyph->get_face()->num_glyphs); + if (charcode_error || glyph_error) { + ft_glyph_warn((FT_ULong)codepoints[n]); + return; + } + + glyph_index = final_glyph_index; // retrieve kerning distance and move pen position if (use_kerning && previous && glyph_index) { FT_Vector delta; - FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); + ft_object_with_glyph->get_kerning(previous, glyph_index, FT_KERNING_DEFAULT, delta); pen.x += delta.x / (hinting_factor << kerning_factor); } - if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { - throw_ft_error("Could not load glyph", error); - } + + // DONE with load_char_with_fallback + // if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { + // throw_ft_error("Could not load glyph", error); + // } + // ignore errors, jump to next glyph // extract glyph image and store it in our table - FT_Glyph thisGlyph; - if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) { - throw_ft_error("Could not get glyph", error); - } + // DONE with load_char_with_fallback + // FT_Glyph thisGlyph; + // if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) { + // throw_ft_error("Could not get glyph", error); + // } // ignore errors, jump to next glyph - last_advance = face->glyph->advance.x; + FT_Glyph &thisGlyph = glyphs[glyphs.size() - 1]; + + // Compare with thisGlyph? + last_advance = ft_object_with_glyph->get_face()->glyph->advance.x; FT_Glyph_Transform(thisGlyph, 0, &pen); FT_Glyph_Transform(thisGlyph, &matrix, 0); xys.push_back(pen.x); @@ -489,7 +560,10 @@ void FT2Font::set_text( pen.x += last_advance; previous = glyph_index; - glyphs.push_back(thisGlyph); + + printf("Set_Text complete! \n"); + // DONE with load_char_with_fallback + // glyphs.push_back(thisGlyph); } FT_Vector_Transform(&pen, &matrix); @@ -513,6 +587,48 @@ void FT2Font::load_char(long charcode, FT_Int32 flags) glyphs.push_back(thisGlyph); } +void FT2Font::load_char_with_fallback(FT2Font* &ft_object_with_glyph, + FT_UInt &final_glyph_index, + std::vector &parent_glyphs, + long charcode, + FT_Int32 flags, + FT_Error &charcode_error, + FT_Error &glyph_error) +{ + printf("loading char!\n"); + FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); + + if (glyph_index) { + printf("glyph found!\n"); + charcode_error = FT_Load_Glyph(face, glyph_index, flags); + // throw_ft_error("Could not load charcode", error); + FT_Glyph thisGlyph; + glyph_error = FT_Get_Glyph(face->glyph, &thisGlyph); + // throw_ft_error("Could not get glyph", error); + final_glyph_index = glyph_index; + parent_glyphs.push_back(thisGlyph); + } + + else { + printf("glyph not found! Fallback size: %lu\n", fallbacks.size()); + for (uint i = 0; i < fallbacks.size(); ++i) { + uint current_size = parent_glyphs.size(); + printf("Fallback %u: %u\n", i, current_size); + fallbacks[i]->load_char_with_fallback(ft_object_with_glyph, final_glyph_index, + parent_glyphs, charcode, flags, charcode_error, + glyph_error); + printf("Got back from fallback load char!\n"); + // jump out if glyph size increased + if (parent_glyphs.size() == current_size + 1) { + printf("size increased!\n"); + ft_object_with_glyph = fallbacks[i]; + printf("Ft object assigned!\n"); + return; + } + } + } +} + void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) { if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { From 67dbcf868f64368ff1cd7c4c6f57890a2acdeab8 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Mon, 26 Jul 2021 04:02:18 +0530 Subject: [PATCH 04/32] Parse multiple fonts for a single FT2Font object --- lib/matplotlib/font_manager.py | 141 +++++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 9d45575eb13d..02c44e781368 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -23,6 +23,7 @@ # - setWeights function needs improvement # - 'light' is an invalid weight value, remove it. +from collections import OrderedDict import dataclasses from functools import lru_cache import json @@ -1308,6 +1309,118 @@ def findfont(self, prop, fontext='ttf', directory=None, prop, fontext, directory, fallback_to_default, rebuild_if_missing, rc_params) + def find_fontsprop(self, prop, fontext='ttf', directory=None, + fallback_to_default=True, rebuild_if_missing=True): + """ + Find font families that most closely matches the given properties. + + Parameters + ---------- + prop : str or `~matplotlib.font_manager.FontProperties` + The font properties to search for. This can be either a + `.FontProperties` object or a string defining a + `fontconfig patterns`_. + + fontext : {'ttf', 'afm'}, default: 'ttf' + The extension of the font file: + + - 'ttf': TrueType and OpenType fonts (.ttf, .ttc, .otf) + - 'afm': Adobe Font Metrics (.afm) + + directory : str, optional + If given, only search this directory and its subdirectories. + + fallback_to_default : bool + If True, will fallback to the default font family (usually + "DejaVu Sans" or "Helvetica") if none of the families were found. + + rebuild_if_missing : bool + Whether to rebuild the font cache and search again if the first + match appears to point to a nonexisting font (i.e., the font cache + contains outdated entries). + + Returns + ------- + OrderedDict + key, value pair of families and their corresponding filepaths. + + Notes + ----- + This is a plugin to original findfont API, which only returns a + single font for given font properties. Instead, this API returns + an OrderedDict containing multiple fonts and their filepaths which + closely match the given font properties. + Since this internally uses original API, there's no change + to the logic of performing the nearest neighbor search. + See `findfont` for more details. + + """ + + rc_params = tuple(tuple(rcParams[key]) for key in [ + "font.serif", "font.sans-serif", "font.cursive", "font.fantasy", + "font.monospace"]) + + prop = FontProperties._from_any(prop) + ffamily = prop.get_family() + + fpaths = OrderedDict() + for fidx in range(len(ffamily)): + cprop = prop.copy() + + # set current prop's family + cprop.set_family(ffamily[fidx]) + + # do not fall back to default font + fpath = self._findfontsprop_cached( + ffamily[fidx], cprop, fontext, directory, + False, rebuild_if_missing, rc_params + ) + if fpath: + fpaths[ffamily[fidx]] = fpath + + # only add default family if no other font was found + # and fallback_to_default is enabled + if not fpaths: + if fallback_to_default: + dfamily = self.defaultFamily[fontext] + cprop = prop.copy().set_family(dfamily) + fpath = self._findfontsprop_cached( + dfamily, cprop, fontext, directory, + True, rebuild_if_missing, rc_params + ) + fpaths[dfamily] = fpath + else: + raise ValueError("Failed to find any font, and fallback " + "to the default font was disabled.") + + return fpaths + + + @lru_cache() + def _findfontsprop_cached( + self, family, prop, fontext, directory, + fallback_to_default, rebuild_if_missing, rc_params + ): + try: + return self._findfont_cached( + prop, fontext, directory, fallback_to_default, + rebuild_if_missing, rc_params + ) + except ValueError: + if not fallback_to_default: + if family.lower() in font_family_aliases: + _log.warning( + "findfont: Generic family %r not found because " + "none of the following families were found: %s", + family, + ", ".join(self._expand_aliases(family)) + ) + else: + _log.warning( + 'findfont: Font family \'%s\' not found.', family + ) + + @lru_cache() def _findfont_cached(self, prop, fontext, directory, fallback_to_default, rebuild_if_missing, rc_params): @@ -1401,9 +1514,23 @@ def is_opentype_cff_font(filename): @lru_cache(64) -def _get_font(filename, hinting_factor, *, _kerning_factor, thread_id): - return ft2font.FT2Font( - filename, hinting_factor, _kerning_factor=_kerning_factor) +def _get_font(fpaths, hinting_factor, *, _kerning_factor, thread_id): + ftobjects = [] + for fpath in fpaths[1:]: + ftobject = ft2font.FT2Font( + fpath, hinting_factor, + _kerning_factor=_kerning_factor + ) + ftobjects.append(ftobject) + + print("\nIn Python:", ftobjects, "\n") + ft2font_object = ft2font.FT2Font( + fpaths[0], hinting_factor, + _fallback_list=ftobjects, + _kerning_factor=_kerning_factor + ) + print("\nBack to Python!\n") + return ft2font_object # FT2Font objects cannot be used across fork()s because they reference the same @@ -1417,11 +1544,14 @@ def _get_font(filename, hinting_factor, *, _kerning_factor, thread_id): def get_font(filename, hinting_factor=None): # Resolving the path avoids embedding the font twice in pdf/ps output if a # single font is selected using two different relative paths. - filename = _cached_realpath(filename) + if isinstance(filename, OrderedDict): + paths = tuple(_cached_realpath(fname) for fname in filename.values()) + else: + paths = (_cached_realpath(filename),) if hinting_factor is None: hinting_factor = rcParams['text.hinting_factor'] # also key on the thread ID to prevent segfaults with multi-threading - return _get_font(filename, hinting_factor, + return _get_font(paths, hinting_factor, _kerning_factor=rcParams['text.kerning_factor'], thread_id=threading.get_ident()) @@ -1446,3 +1576,4 @@ def _load_fontmanager(*, try_read_cache=True): fontManager = _load_fontmanager() findfont = fontManager.findfont +find_fontsprop = fontManager.find_fontsprop From 69cf28bfbf4f11cb8c90ef7b6cc03ada26bf8762 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Mon, 26 Jul 2021 04:03:21 +0530 Subject: [PATCH 05/32] Trigger font fallback for Agg backend --- lib/matplotlib/backends/backend_agg.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index b937c64fce95..f15b7a2d1381 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -35,7 +35,7 @@ from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, RendererBase) -from matplotlib.font_manager import findfont, get_font +from matplotlib.font_manager import find_fontsprop, get_font from matplotlib.ft2font import (LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING, LOAD_DEFAULT, LOAD_NO_AUTOHINT) from matplotlib.mathtext import MathTextParser @@ -186,6 +186,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # We pass '0' for angle here, since it will be rotated (in raster # space) in the following call to draw_text_image). font.set_text(s, 0, flags=flags) + font.draw_glyphs_to_bitmap( antialiased=mpl.rcParams['text.antialiased']) d = font.get_descent() / 64.0 @@ -251,7 +252,7 @@ def _get_agg_font(self, prop): """ Get the font for text instance t, caching for efficiency """ - fname = findfont(prop) + fname = find_fontsprop(prop) font = get_font(fname) font.clear() From d5aef822bcb89052de6b72334529509cd382ea2f Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 05:38:22 +0530 Subject: [PATCH 06/32] Remove prints --- lib/matplotlib/backends/backend_agg.py | 1 - lib/matplotlib/font_manager.py | 4 --- src/ft2font.cpp | 39 -------------------------- src/ft2font.h | 1 - src/ft2font_wrapper.cpp | 3 -- 5 files changed, 48 deletions(-) diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index f15b7a2d1381..b5a2f85affc1 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -186,7 +186,6 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # We pass '0' for angle here, since it will be rotated (in raster # space) in the following call to draw_text_image). font.set_text(s, 0, flags=flags) - font.draw_glyphs_to_bitmap( antialiased=mpl.rcParams['text.antialiased']) d = font.get_descent() / 64.0 diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 02c44e781368..aca91f25d7e4 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -1395,7 +1395,6 @@ def find_fontsprop(self, prop, fontext='ttf', directory=None, return fpaths - @lru_cache() def _findfontsprop_cached( self, family, prop, fontext, directory, @@ -1420,7 +1419,6 @@ def _findfontsprop_cached( 'findfont: Font family \'%s\' not found.', family ) - @lru_cache() def _findfont_cached(self, prop, fontext, directory, fallback_to_default, rebuild_if_missing, rc_params): @@ -1523,13 +1521,11 @@ def _get_font(fpaths, hinting_factor, *, _kerning_factor, thread_id): ) ftobjects.append(ftobject) - print("\nIn Python:", ftobjects, "\n") ft2font_object = ft2font.FT2Font( fpaths[0], hinting_factor, _fallback_list=ftobjects, _kerning_factor=_kerning_factor ) - print("\nBack to Python!\n") return ft2font_object diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 0b52c72551db..179256f5208d 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -396,13 +396,6 @@ void FT2Font::clear() } } -void FT2Font::check() { - printf("Fallback num: -1; Numface: %lu\n", get_face()->num_glyphs); - for (uint i = 0; i < fallbacks.size(); i++) { - printf("Fallback num: %u; Numface: %lu\n", i, fallbacks[i]->get_face()->num_glyphs); - } -} - void FT2Font::set_size(double ptsize, double dpi) { FT_Error error = FT_Set_Char_Size( @@ -481,7 +474,6 @@ void FT2Font::set_text( size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector &xys) { FT_Matrix matrix; /* transformation matrix */ - check(); angle = angle / 360.0 * 2 * M_PI; @@ -507,10 +499,8 @@ void FT2Font::set_text( FT_UInt final_glyph_index; FT_Error charcode_error, glyph_error; FT2Font *ft_object_with_glyph = this; - printf("\nBefore loading char! \n"); load_char_with_fallback(ft_object_with_glyph, final_glyph_index, glyphs, codepoints[n], flags, charcode_error, glyph_error); - printf("Final ft2font: %lu\n", ft_object_with_glyph->get_face()->num_glyphs); if (charcode_error || glyph_error) { ft_glyph_warn((FT_ULong)codepoints[n]); return; @@ -525,25 +515,9 @@ void FT2Font::set_text( pen.x += delta.x / (hinting_factor << kerning_factor); } - // DONE with load_char_with_fallback - // if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { - // throw_ft_error("Could not load glyph", error); - // } - - // ignore errors, jump to next glyph - // extract glyph image and store it in our table - - // DONE with load_char_with_fallback - // FT_Glyph thisGlyph; - // if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) { - // throw_ft_error("Could not get glyph", error); - // } - // ignore errors, jump to next glyph - FT_Glyph &thisGlyph = glyphs[glyphs.size() - 1]; - // Compare with thisGlyph? last_advance = ft_object_with_glyph->get_face()->glyph->advance.x; FT_Glyph_Transform(thisGlyph, 0, &pen); FT_Glyph_Transform(thisGlyph, &matrix, 0); @@ -560,10 +534,6 @@ void FT2Font::set_text( pen.x += last_advance; previous = glyph_index; - - printf("Set_Text complete! \n"); - // DONE with load_char_with_fallback - // glyphs.push_back(thisGlyph); } FT_Vector_Transform(&pen, &matrix); @@ -595,34 +565,25 @@ void FT2Font::load_char_with_fallback(FT2Font* &ft_object_with_glyph, FT_Error &charcode_error, FT_Error &glyph_error) { - printf("loading char!\n"); FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); if (glyph_index) { - printf("glyph found!\n"); charcode_error = FT_Load_Glyph(face, glyph_index, flags); - // throw_ft_error("Could not load charcode", error); FT_Glyph thisGlyph; glyph_error = FT_Get_Glyph(face->glyph, &thisGlyph); - // throw_ft_error("Could not get glyph", error); final_glyph_index = glyph_index; parent_glyphs.push_back(thisGlyph); } else { - printf("glyph not found! Fallback size: %lu\n", fallbacks.size()); for (uint i = 0; i < fallbacks.size(); ++i) { uint current_size = parent_glyphs.size(); - printf("Fallback %u: %u\n", i, current_size); fallbacks[i]->load_char_with_fallback(ft_object_with_glyph, final_glyph_index, parent_glyphs, charcode, flags, charcode_error, glyph_error); - printf("Got back from fallback load char!\n"); // jump out if glyph size increased if (parent_glyphs.size() == current_size + 1) { - printf("size increased!\n"); ft_object_with_glyph = fallbacks[i]; - printf("Ft object assigned!\n"); return; } } diff --git a/src/ft2font.h b/src/ft2font.h index b09ad801bd34..a656f08c5585 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -75,7 +75,6 @@ class FT2Font void set_size(double ptsize, double dpi); void set_charmap(int i); void set_fallbacks(std::vector &fallback_list); - void check(); void select_charmap(unsigned long i); void set_text( size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector &xys); diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 634f1dd52986..118b39bd079d 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -362,7 +362,6 @@ const char *PyFT2Font_init__doc__ = static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds) { - printf("PyFT2Font init!\n"); PyObject *filename = NULL, *open = NULL, *data = NULL, *fallback_list = NULL; FT_Open_Args open_args; long hinting_factor = 8; @@ -399,7 +398,6 @@ static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds) Py_INCREF(fallback_list); } - printf("Fallback SIZE: %lu \n", self->fallbacks.size()); if (PyBytes_Check(filename) || PyUnicode_Check(filename)) { if (!(open = PyDict_GetItemString(PyEval_GetBuiltins(), "open")) // Borrowed reference. @@ -463,7 +461,6 @@ static PyObject *PyFT2Font_set_size(PyFT2Font *self, PyObject *args, PyObject *k { double ptsize; double dpi; - printf("PyFT, set_size called!\n"); if (!PyArg_ParseTuple(args, "dd:set_size", &ptsize, &dpi)) { return NULL; From aa82ecddc4cc590d059ef38a0d86ba63baab1a8f Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 06:18:48 +0530 Subject: [PATCH 07/32] Cleanup wrapper --- src/forward.h | 5 + src/ft2font.cpp | 227 ++++++++++++++++++++++++++++++++++------ src/ft2font.h | 43 ++++++-- src/ft2font_wrapper.cpp | 213 ++++++++++++++++++++++++++++++------- 4 files changed, 413 insertions(+), 75 deletions(-) create mode 100644 src/forward.h diff --git a/src/forward.h b/src/forward.h new file mode 100644 index 000000000000..dd2f71e1d6c3 --- /dev/null +++ b/src/forward.h @@ -0,0 +1,5 @@ +#pragma once + +struct PyFT2Font; + +class FT2Font; diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 179256f5208d..ee2650475542 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -325,10 +325,12 @@ FT2Font::get_path() FT2Font::FT2Font(FT_Open_Args &open_args, long hinting_factor_, - std::vector &fallback_list) + std::vector &fallback_list, + PyFT2Font *py_pointer) : image(), face(NULL) { clear(); + py_font = py_pointer; FT_Error error = FT_Open_Face(_ft2Library, &open_args, 0, &face); @@ -372,12 +374,9 @@ FT2Font::~FT2Font() } if (face) { + printf("Deleting face from: %lu\n", face->num_glyphs); FT_Done_Face(face); } - - for (uint i = 0; i < fallbacks.size(); i ++) { - fallbacks[i]->~FT2Font(); - } } void FT2Font::clear() @@ -390,6 +389,8 @@ void FT2Font::clear() } glyphs.clear(); + glyph_to_font.clear(); + char_to_font.clear(); for (uint i = 0; i < fallbacks.size(); i ++) { fallbacks[i]->clear(); @@ -435,8 +436,19 @@ void FT2Font::select_charmap(unsigned long i) } } -int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode) +int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, bool fallback = false) { + if (fallback && glyph_to_font.find(left) != glyph_to_font.end() && + glyph_to_font.find(right) != glyph_to_font.end()) { + FT2Font *left_ft_object = glyph_to_font[left]; + FT2Font *right_ft_object = glyph_to_font[right]; + if (left_ft_object != right_ft_object) { + printf("Prev FT2Font != Curr FT2Font!\n"); + } + // if left_ft_object is not the same the right_ft_object, + // do the exact same thing which set_text does. + return right_ft_object->get_kerning(left, right, mode, false); + } if (!FT_HAS_KERNING(face)) { return 0; } @@ -499,11 +511,12 @@ void FT2Font::set_text( FT_UInt final_glyph_index; FT_Error charcode_error, glyph_error; FT2Font *ft_object_with_glyph = this; - load_char_with_fallback(ft_object_with_glyph, final_glyph_index, glyphs, codepoints[n], - flags, charcode_error, glyph_error); - if (charcode_error || glyph_error) { + bool was_found = load_char_with_fallback(ft_object_with_glyph, final_glyph_index, glyphs, + char_to_font, glyph_to_font, codepoints[n], flags, + charcode_error, glyph_error, false); + if (!was_found) { ft_glyph_warn((FT_ULong)codepoints[n]); - return; + continue; } glyph_index = final_glyph_index; @@ -544,50 +557,175 @@ void FT2Font::set_text( } } -void FT2Font::load_char(long charcode, FT_Int32 flags) +void FT2Font::fill_glyphs( + size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, bool warn = false) { - FT_UInt glyph_index = ft_get_char_index_or_warn(face, (FT_ULong)charcode); - if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { - throw_ft_error("Could not load charcode", error); + FT_Matrix matrix; /* transformation matrix */ + + angle = angle / 360.0 * 2 * M_PI; + + // this computes width and height in subpixels so we have to divide by 64 + matrix.xx = (FT_Fixed)(cos(angle) * 0x10000L); + matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000L); + matrix.yx = (FT_Fixed)(sin(angle) * 0x10000L); + matrix.yy = (FT_Fixed)(cos(angle) * 0x10000L); + + FT_Bool use_kerning = FT_HAS_KERNING(face); + FT_UInt previous = 0; + + clear(); + + bbox.xMin = bbox.yMin = 32000; + bbox.xMax = bbox.yMax = -32000; + + for (unsigned int n = 0; n < N; n++) { + FT_UInt glyph_index; + FT_BBox glyph_bbox; + FT_Pos last_advance; + + FT_UInt final_glyph_index; + FT_Error charcode_error, glyph_error; + FT2Font *ft_object_with_glyph = this; + bool was_found = load_char_with_fallback(ft_object_with_glyph, final_glyph_index, glyphs, + char_to_font, glyph_to_font, codepoints[n], flags, + charcode_error, glyph_error, false); + if (!was_found) { + if (warn) ft_glyph_warn((FT_ULong)codepoints[n]); + continue; + } + + glyph_index = final_glyph_index; + + // retrieve kerning distance and move pen position + if (use_kerning && previous && glyph_index) { + FT_Vector delta; + ft_object_with_glyph->get_kerning(previous, glyph_index, FT_KERNING_DEFAULT, delta); + pen.x += delta.x / (hinting_factor << kerning_factor); + } + + // extract glyph image and store it in our table + + FT_Glyph &thisGlyph = glyphs[glyphs.size() - 1]; + + last_advance = ft_object_with_glyph->get_face()->glyph->advance.x; + FT_Glyph_Transform(thisGlyph, 0, &pen); + FT_Glyph_Transform(thisGlyph, &matrix, 0); + + FT_Glyph_Get_CBox(thisGlyph, FT_GLYPH_BBOX_SUBPIXELS, &glyph_bbox); + + bbox.xMin = std::min(bbox.xMin, glyph_bbox.xMin); + bbox.xMax = std::max(bbox.xMax, glyph_bbox.xMax); + bbox.yMin = std::min(bbox.yMin, glyph_bbox.yMin); + bbox.yMax = std::max(bbox.yMax, glyph_bbox.yMax); + + pen.x += last_advance; + + previous = glyph_index; } - FT_Glyph thisGlyph; - if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) { - throw_ft_error("Could not get glyph", error); + + FT_Vector_Transform(&pen, &matrix); + advance = pen.x; + + if (bbox.xMin > bbox.xMax) { + bbox.xMin = bbox.yMin = bbox.xMax = bbox.yMax = 0; } - glyphs.push_back(thisGlyph); } -void FT2Font::load_char_with_fallback(FT2Font* &ft_object_with_glyph, +void FT2Font::load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool fallback = false) +{ + // if this is parent FT2Font, cache will be filled in 2 ways: + // 1. set_text was previously called + // 2. set_text was not called and fallback was enabled + if (fallback && char_to_font.find(charcode) != char_to_font.end()) { + ft_object = char_to_font[charcode]; + // since it will be assigned to this object anyway + FT2Font *throwaway = NULL; + ft_object->load_char(charcode, flags, throwaway, false); + } else if (fallback) { + FT_UInt final_glyph_index; + FT_Error charcode_error, glyph_error; + FT2Font *ft_object_with_glyph = this; + bool was_found = load_char_with_fallback(ft_object_with_glyph, final_glyph_index, glyphs, char_to_font, + glyph_to_font, charcode, flags, charcode_error, glyph_error, true); + if (!was_found) { + ft_glyph_warn(charcode); + if (charcode_error) throw_ft_error("Could not load charcode", charcode_error); + else if (glyph_error) throw_ft_error("Could not load charcode", glyph_error); + } + ft_object = ft_object_with_glyph; + } else { + ft_object = this; + FT_UInt glyph_index = ft_get_char_index_or_warn(face, (FT_ULong)charcode); + + if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { + throw_ft_error("Could not load charcode", error); + } + FT_Glyph thisGlyph; + if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) { + throw_ft_error("Could not get glyph", error); + } + glyphs.push_back(thisGlyph); + } +} + +bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, FT_UInt &final_glyph_index, std::vector &parent_glyphs, + std::unordered_map &parent_char_to_font, + std::unordered_map &parent_glyph_to_font, long charcode, FT_Int32 flags, FT_Error &charcode_error, - FT_Error &glyph_error) + FT_Error &glyph_error, + bool override = false) { FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); - if (glyph_index) { + if (glyph_index || override) { charcode_error = FT_Load_Glyph(face, glyph_index, flags); FT_Glyph thisGlyph; glyph_error = FT_Get_Glyph(face->glyph, &thisGlyph); + + if (charcode_error || glyph_error) { + return false; + } final_glyph_index = glyph_index; + + // cache the result for future + // need to store this for anytime a character is loaded from a parent + // FT2Font object or to generate a mapping of individual characters to fonts + ft_object_with_glyph = this; + parent_glyph_to_font[final_glyph_index] = this; + parent_char_to_font[charcode] = this; parent_glyphs.push_back(thisGlyph); + return true; } else { for (uint i = 0; i < fallbacks.size(); ++i) { - uint current_size = parent_glyphs.size(); - fallbacks[i]->load_char_with_fallback(ft_object_with_glyph, final_glyph_index, - parent_glyphs, charcode, flags, charcode_error, - glyph_error); - // jump out if glyph size increased - if (parent_glyphs.size() == current_size + 1) { - ft_object_with_glyph = fallbacks[i]; - return; - } + bool was_found = fallbacks[i]->load_char_with_fallback( + ft_object_with_glyph, final_glyph_index, parent_glyphs, parent_char_to_font, + parent_glyph_to_font, charcode, flags, charcode_error, glyph_error, override); + if (was_found) + return true; } + return false; + } +} + +void FT2Font::load_glyph(FT_UInt glyph_index, + FT_Int32 flags, + FT2Font *&ft_object, + bool fallback = false) +{ + // cache is only for parent FT2Font + if (fallback && glyph_to_font.find(glyph_index) != glyph_to_font.end()) { + ft_object = glyph_to_font[glyph_index]; + } else { + ft_object = this; } + + ft_object->load_glyph(glyph_index, flags); } void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) @@ -602,6 +740,27 @@ void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) glyphs.push_back(thisGlyph); } +FT_UInt FT2Font::get_char_index(FT_ULong charcode, bool fallback = false) +{ + FT2Font *ft_object = NULL; + if (fallback && char_to_font.find(charcode) != char_to_font.end()) { + // fallback denotes whether we want to search fallback list. + // should call set_text/load_char_with_fallback to parent FT2Font before + // wanting to use fallback list here. (since that populates the cache) + ft_object = char_to_font[charcode]; + } else { + // set as self + ft_object = this; + } + + return ft_get_char_index_or_warn(ft_object->get_face(), charcode); +} + +void FT2Font::get_cbox(FT_BBox &bbox) +{ + FT_Glyph_Get_CBox(glyphs.back(), ft_glyph_bbox_subpixels, &bbox); +} + void FT2Font::get_width_height(long *width, long *height) { *width = advance; @@ -692,8 +851,14 @@ void FT2Font::draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, im.draw_bitmap(&bitmap->bitmap, x + bitmap->left, y); } -void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer) +void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer, bool fallback = false) { + if (fallback && glyph_to_font.find(glyph_number) != glyph_to_font.end()) { + // cache is only for parent FT2Font + FT2Font *ft_object = glyph_to_font[glyph_number]; + ft_object->get_glyph_name(glyph_number, buffer, false); + return; + } if (!FT_HAS_GLYPH_NAMES(face)) { /* Note that this generated name must match the name that is generated by ttconv in ttfont_CharStrings_getname. */ diff --git a/src/ft2font.h b/src/ft2font.h index a656f08c5585..20787ec61e7f 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -1,10 +1,13 @@ /* -*- mode: c++; c-basic-offset: 4 -*- */ /* A python interface to FreeType */ +#pragma once +#include "forward.h" #ifndef MPL_FT2FONT_H #define MPL_FT2FONT_H #include #include +#include extern "C" { #include @@ -69,7 +72,7 @@ class FT2Font { public: - FT2Font(FT_Open_Args &open_args, long hinting_factor, std::vector &fallback_list); + FT2Font(FT_Open_Args &open_args, long hinting_factor, std::vector &fallback_list, PyFT2Font *py_pointer); virtual ~FT2Font(); void clear(); void set_size(double ptsize, double dpi); @@ -78,18 +81,25 @@ class FT2Font void select_charmap(unsigned long i); void set_text( size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector &xys); - int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode); + void fill_glyphs( + size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, bool warn); + int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, bool fallback); int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &delta); void set_kerning_factor(int factor); - void load_char(long charcode, FT_Int32 flags); - void load_char_with_fallback(FT2Font* &ft_object_with_glyph, + void load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool fallback); + bool load_char_with_fallback(FT2Font *&ft_object_with_glyph, FT_UInt &final_glyph_index, std::vector &parent_glyphs, + std::unordered_map &parent_char_to_font, + std::unordered_map &parent_glyph_to_font, long charcode, FT_Int32 flags, FT_Error &charcode_error, - FT_Error &glyph_error); + FT_Error &glyph_error, + bool override); + void load_glyph(FT_UInt glyph_index, FT_Int32 flags, FT2Font *&ft_object, bool fallback); void load_glyph(FT_UInt glyph_index, FT_Int32 flags); + void load_parent_glyph(std::vector &parent_glyphs, long charcode, FT_Int32 flags); void get_width_height(long *width, long *height); void get_bitmap_offset(long *x, long *y); long get_descent(); @@ -98,14 +108,32 @@ class FT2Font void get_xys(bool antialiased, std::vector &xys); void draw_glyphs_to_bitmap(bool antialiased); void draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, bool antialiased); - void get_glyph_name(unsigned int glyph_number, char *buffer); + void get_glyph_name(unsigned int glyph_number, char *buffer, bool fallback); long get_name_index(char *name); + FT_UInt get_char_index(FT_ULong charcode, bool fallback); + void get_cbox(FT_BBox &bbox); PyObject* get_path(); FT_Face &get_face() { return face; } + + PyFT2Font *get_pyfont() + { + return py_font; + } + + std::unordered_map &get_glyph_to_font() + { + return glyph_to_font; + } + + std::unordered_map &get_char_to_font() + { + return char_to_font; + } + FT2Image &get_image() { return image; @@ -133,6 +161,9 @@ class FT2Font FT_Vector pen; /* untransformed origin */ std::vector glyphs; std::vector fallbacks; + std::unordered_map glyph_to_font; + std::unordered_map char_to_font; + PyFT2Font *py_font; FT_BBox bbox; FT_Pos advance; long hinting_factor; diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 118b39bd079d..b08e937e3aa8 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -1,3 +1,4 @@ +#include "forward.h" #include "mplutils.h" #include "ft2font.h" #include "py_converters.h" @@ -178,25 +179,25 @@ typedef struct static PyTypeObject PyGlyphType; -static PyObject * -PyGlyph_new(const FT_Face &face, const FT_Glyph &glyph, size_t ind, long hinting_factor) +static PyObject *PyGlyph_new(FT2Font *&parent_ft_object, FT2Font *&ft_object) { PyGlyph *self; self = (PyGlyph *)PyGlyphType.tp_alloc(&PyGlyphType, 0); - - self->glyphInd = ind; - - FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_subpixels, &self->bbox); - - self->width = face->glyph->metrics.width / hinting_factor; - self->height = face->glyph->metrics.height; - self->horiBearingX = face->glyph->metrics.horiBearingX / hinting_factor; - self->horiBearingY = face->glyph->metrics.horiBearingY; - self->horiAdvance = face->glyph->metrics.horiAdvance; - self->linearHoriAdvance = face->glyph->linearHoriAdvance / hinting_factor; - self->vertBearingX = face->glyph->metrics.vertBearingX; - self->vertBearingY = face->glyph->metrics.vertBearingY; - self->vertAdvance = face->glyph->metrics.vertAdvance; + const long hinting_factor = ft_object->get_hinting_factor(); + + self->glyphInd = ft_object->get_last_glyph_index(); + + ft_object->get_cbox(self->bbox); + + self->width = ft_object->get_face()->glyph->metrics.width / hinting_factor; + self->height = ft_object->get_face()->glyph->metrics.height; + self->horiBearingX = ft_object->get_face()->glyph->metrics.horiBearingX / hinting_factor; + self->horiBearingY = ft_object->get_face()->glyph->metrics.horiBearingY; + self->horiAdvance = ft_object->get_face()->glyph->metrics.horiAdvance; + self->linearHoriAdvance = ft_object->get_face()->glyph->linearHoriAdvance / hinting_factor; + self->vertBearingX = ft_object->get_face()->glyph->metrics.vertBearingX; + self->vertBearingY = ft_object->get_face()->glyph->metrics.vertBearingY; + self->vertAdvance = ft_object->get_face()->glyph->metrics.vertAdvance; return (PyObject *)self; } @@ -254,7 +255,7 @@ static PyTypeObject *PyGlyph_init_type(PyObject *m, PyTypeObject *type) * FT2Font * */ -typedef struct +struct PyFT2Font { PyObject_HEAD FT2Font *x; @@ -265,7 +266,7 @@ typedef struct Py_ssize_t strides[2]; Py_ssize_t suboffsets[2]; std::vector fallbacks; -} PyFT2Font; +}; static unsigned long read_from_file_callback(FT_Stream stream, unsigned long offset, @@ -420,7 +421,7 @@ static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds) Py_CLEAR(data); CALL_CPP_FULL( - "FT2Font", (self->x = new FT2Font(open_args, hinting_factor, self->fallbacks)), + "FT2Font", (self->x = new FT2Font(open_args, hinting_factor, self->fallbacks, self)), Py_CLEAR(self->py_file), -1); CALL_CPP_INIT("FT2Font->set_kerning_factor", (self->x->set_kerning_factor(kerning_factor))); @@ -520,12 +521,13 @@ static PyObject *PyFT2Font_get_kerning(PyFT2Font *self, PyObject *args, PyObject { FT_UInt left, right, mode; int result; + int fallback = 1; if (!PyArg_ParseTuple(args, "III:get_kerning", &left, &right, &mode)) { return NULL; } - CALL_CPP("get_kerning", (result = self->x->get_kerning(left, right, mode))); + CALL_CPP("get_kerning", (result = self->x->get_kerning(left, right, mode, (bool)fallback))); return PyLong_FromLong(result); } @@ -602,6 +604,78 @@ static PyObject *PyFT2Font_set_text(PyFT2Font *self, PyObject *args, PyObject *k return convert_xys_to_array(xys); } +const char *PyFT2Font_fill_glyphs__doc__ = + "fill_glyphs(string, angle, flags=32)\n" + "--\n\n" + "Fill (and cache) a text *string* and *angle*.\n" + "*flags* can be a bitwise-or of the LOAD_XXX constants;\n" + "the default value is LOAD_FORCE_AUTOHINT.\n" + "A mapping of character to FT2Font object is returned.\n"; + +static PyObject *PyFT2Font_fill_glyphs(PyFT2Font *self, PyObject *args, PyObject *kwds) +{ + PyObject *textobj; + double angle = 0.0; + bool warn = false; + FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; + const char *names[] = { "string", "angle", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "O|di:fill_glyphs", (char **)names, &textobj, &angle, &flags)) { + return NULL; + } + + std::vector codepoints; + size_t size; + + if (PyUnicode_Check(textobj)) { + size = PyUnicode_GET_LENGTH(textobj); + codepoints.resize(size); +#if defined(PYPY_VERSION) && (PYPY_VERSION_NUM < 0x07040000) + // PyUnicode_ReadChar is available from PyPy 7.3.2, but wheels do not + // specify the micro-release version, so put the version bound at 7.4 + // to prevent generating wheels unusable on PyPy 7.3.{0,1}. + Py_UNICODE *unistr = PyUnicode_AsUnicode(textobj); + for (size_t i = 0; i < size; ++i) { + codepoints[i] = unistr[i]; + } +#else + for (size_t i = 0; i < size; ++i) { + codepoints[i] = PyUnicode_ReadChar(textobj, i); + } +#endif + } else { + PyErr_SetString(PyExc_TypeError, "String must be str"); + return NULL; + } + + uint32_t* codepoints_array = NULL; + if (size > 0) { + codepoints_array = &codepoints[0]; + } + CALL_CPP("fill_glyphs", self->x->fill_glyphs(size, codepoints_array, angle, flags, warn)); + + PyObject *char_to_font; + if (!(char_to_font = PyDict_New())) { + return NULL; + } + std::unordered_map from_ft = self->x->get_char_to_font(); + + for (std::pair &itr : from_ft) { + PyObject *key = NULL, *val = NULL; + bool error = (!(key = PyLong_FromLong(itr.first)) + || !(val = reinterpret_cast(itr.second->get_pyfont())) + || (PyDict_SetItem(char_to_font, key, val) == -1)); + Py_XDECREF(key); + Py_XDECREF(val); + if (error) { + Py_DECREF(char_to_font); + return NULL; + } + } + return char_to_font; +} + const char *PyFT2Font_get_num_glyphs__doc__ = "get_num_glyphs()\n" "--\n\n" @@ -612,8 +686,66 @@ static PyObject *PyFT2Font_get_num_glyphs(PyFT2Font *self, PyObject *args, PyObj return PyLong_FromLong(self->x->get_num_glyphs()); } +const char *PyFT2Font_get_char_to_font__doc__ = + "get_char_to_font()\n" + "--\n\n" + "Return a cache of characters mapped to FT2Font objects.\n"; + +static PyObject *PyFT2Font_get_char_to_font(PyFT2Font *self, PyObject *args, PyObject *kwds) +{ + PyObject *char_to_font; + if (!(char_to_font = PyDict_New())) { + return NULL; + } + std::unordered_map from_ft = self->x->get_char_to_font(); + + for (std::pair &itr : from_ft) { + PyObject *key = NULL, *val = NULL; + bool error = (!(key = PyLong_FromLong(itr.first)) + || !(val = reinterpret_cast(itr.second->get_pyfont())) + || (PyDict_SetItem(char_to_font, key, val) == -1)); + Py_XDECREF(key); + Py_XDECREF(val); + if (error) { + Py_DECREF(char_to_font); + return NULL; + } + } + return char_to_font; +} + +const char *PyFT2Font_get_glyph_to_font__doc__ = + "get_glyph_to_font()\n" + "--\n\n" + "Return a cache of glyph indexes mapped to FT2Font objects.\n"; + +static PyObject *PyFT2Font_get_glyph_to_font(PyFT2Font *self, PyObject *args, PyObject *kwds) +{ + PyObject *glyph_to_font; + if (!(glyph_to_font = PyDict_New())) { + return NULL; + } + std::unordered_map from_ft = self->x->get_glyph_to_font(); + + for (std::pair &itr : from_ft) { + PyObject *key = NULL, *val = NULL; + bool error = (!(key = PyLong_FromLong(itr.first)) + || !(val = reinterpret_cast(itr.second->get_pyfont())) + || (PyDict_SetItem(glyph_to_font, key, val) == -1)); + // Py_XDECREF(key); + // Py_XDECREF(val); + Py_INCREF(val); + Py_INCREF(key); + if (error) { + Py_DECREF(glyph_to_font); + return NULL; + } + } + return glyph_to_font; +} + const char *PyFT2Font_load_char__doc__ = - "load_char(charcode, flags=32)\n" + "load_char(charcode, fallback=False, flags=32)\n" "--\n\n" "Load character with *charcode* in current fontfile and set glyph.\n" "*flags* can be a bitwise-or of the LOAD_XXX constants;\n" @@ -632,23 +764,22 @@ const char *PyFT2Font_load_char__doc__ = static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject *kwds) { long charcode; + int fallback = 1; FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; const char *names[] = { "charcode", "flags", NULL }; /* This makes a technically incorrect assumption that FT_Int32 is int. In theory it can also be long, if the size of int is less than 32 bits. This is very unlikely on modern platforms. */ - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "l|i:load_char", (char **)names, &charcode, &flags)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|i:load_char", (char **)names, &charcode, + &flags)) { return NULL; } - CALL_CPP("load_char", (self->x->load_char(charcode, flags))); + FT2Font *ft_object = NULL; + CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); - return PyGlyph_new(self->x->get_face(), - self->x->get_last_glyph(), - self->x->get_last_glyph_index(), - self->x->get_hinting_factor()); + return PyGlyph_new(self->x, ft_object); } const char *PyFT2Font_load_glyph__doc__ = @@ -672,22 +803,21 @@ static PyObject *PyFT2Font_load_glyph(PyFT2Font *self, PyObject *args, PyObject { FT_UInt glyph_index; FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; + int fallback = 1; const char *names[] = { "glyph_index", "flags", NULL }; /* This makes a technically incorrect assumption that FT_Int32 is int. In theory it can also be long, if the size of int is less than 32 bits. This is very unlikely on modern platforms. */ - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "I|i:load_glyph", (char **)names, &glyph_index, &flags)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "I|i:load_glyph", (char **)names, &glyph_index, + &flags)) { return NULL; } - CALL_CPP("load_glyph", (self->x->load_glyph(glyph_index, flags))); + FT2Font *ft_object = NULL; + CALL_CPP("load_glyph", (self->x->load_glyph(glyph_index, flags, ft_object, (bool)fallback))); - return PyGlyph_new(self->x->get_face(), - self->x->get_last_glyph(), - self->x->get_last_glyph_index(), - self->x->get_hinting_factor()); + return PyGlyph_new(self->x, ft_object); } const char *PyFT2Font_get_width_height__doc__ = @@ -833,10 +963,13 @@ static PyObject *PyFT2Font_get_glyph_name(PyFT2Font *self, PyObject *args, PyObj { unsigned int glyph_number; char buffer[128]; + int fallback = 1; + + // parse kwds too if (!PyArg_ParseTuple(args, "I:get_glyph_name", &glyph_number)) { return NULL; } - CALL_CPP("get_glyph_name", (self->x->get_glyph_name(glyph_number, buffer))); + CALL_CPP("get_glyph_name", (self->x->get_glyph_name(glyph_number, buffer, (bool)fallback))); return PyUnicode_FromString(buffer); } @@ -872,7 +1005,7 @@ static PyObject *PyFT2Font_get_charmap(PyFT2Font *self, PyObject *args, PyObject const char *PyFT2Font_get_char_index__doc__ = - "get_char_index(codepoint)\n" + "get_char_index(codepoint, fallback=True)\n" "--\n\n" "Return the glyph index corresponding to a character *codepoint*.\n"; @@ -880,12 +1013,13 @@ static PyObject *PyFT2Font_get_char_index(PyFT2Font *self, PyObject *args, PyObj { FT_UInt index; FT_ULong ccode; + int fallback = 1; if (!PyArg_ParseTuple(args, "k:get_char_index", &ccode)) { return NULL; } - index = FT_Get_Char_Index(self->x->get_face(), ccode); + CALL_CPP("get_char_index", index = self->x->get_char_index(ccode, (bool)fallback)); return PyLong_FromLong(index); } @@ -1502,7 +1636,10 @@ static PyTypeObject *PyFT2Font_init_type(PyObject *m, PyTypeObject *type) {"select_charmap", (PyCFunction)PyFT2Font_select_charmap, METH_VARARGS, PyFT2Font_select_charmap__doc__}, {"get_kerning", (PyCFunction)PyFT2Font_get_kerning, METH_VARARGS, PyFT2Font_get_kerning__doc__}, {"set_text", (PyCFunction)PyFT2Font_set_text, METH_VARARGS|METH_KEYWORDS, PyFT2Font_set_text__doc__}, + {"fill_glyphs", (PyCFunction)PyFT2Font_fill_glyphs, METH_VARARGS|METH_KEYWORDS, PyFT2Font_fill_glyphs__doc__}, {"get_num_glyphs", (PyCFunction)PyFT2Font_get_num_glyphs, METH_NOARGS, PyFT2Font_get_num_glyphs__doc__}, + {"get_char_to_font", (PyCFunction)PyFT2Font_get_char_to_font, METH_NOARGS, PyFT2Font_get_char_to_font__doc__}, + {"get_glyph_to_font", (PyCFunction)PyFT2Font_get_glyph_to_font, METH_NOARGS, PyFT2Font_get_glyph_to_font__doc__}, {"load_char", (PyCFunction)PyFT2Font_load_char, METH_VARARGS|METH_KEYWORDS, PyFT2Font_load_char__doc__}, {"load_glyph", (PyCFunction)PyFT2Font_load_glyph, METH_VARARGS|METH_KEYWORDS, PyFT2Font_load_glyph__doc__}, {"get_width_height", (PyCFunction)PyFT2Font_get_width_height, METH_NOARGS, PyFT2Font_get_width_height__doc__}, From bea7a2d01ebb671168aa5cdfd519e4e58d56748e Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 06:22:15 +0530 Subject: [PATCH 08/32] Remove stale prints --- src/ft2font.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index ee2650475542..d97dc9cf5d65 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -374,7 +374,6 @@ FT2Font::~FT2Font() } if (face) { - printf("Deleting face from: %lu\n", face->num_glyphs); FT_Done_Face(face); } } From 51173986f27a7d2b73e78a1c59e968eb5d9dee3a Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 06:43:11 +0530 Subject: [PATCH 09/32] Do not warn for get_char_index --- src/ft2font.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index d97dc9cf5d65..13e45b3ab8ee 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -185,13 +185,14 @@ static FT_UInt ft_glyph_warn(FT_ULong charcode) return 0; } -static FT_UInt ft_get_char_index_or_warn(FT_Face face, FT_ULong charcode) +static FT_UInt ft_get_char_index_or_warn(FT_Face face, FT_ULong charcode, bool warn = true) { FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); if (glyph_index) { return glyph_index; } - return ft_glyph_warn(charcode); + if (warn) return ft_glyph_warn(charcode); + else return 0; } // ft_outline_decomposer should be passed to FT_Outline_Decompose. On the @@ -752,7 +753,8 @@ FT_UInt FT2Font::get_char_index(FT_ULong charcode, bool fallback = false) ft_object = this; } - return ft_get_char_index_or_warn(ft_object->get_face(), charcode); + // historically, get_char_index never raises a warning + return ft_get_char_index_or_warn(ft_object->get_face(), charcode, false); } void FT2Font::get_cbox(FT_BBox &bbox) From fc413dc243d07f16742c15e1d54f2f635cacca9b Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 06:55:14 +0530 Subject: [PATCH 10/32] Left != Right kerning comment --- src/ft2font.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 13e45b3ab8ee..fdabb0bd2034 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -443,7 +443,7 @@ int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, bool fallbac FT2Font *left_ft_object = glyph_to_font[left]; FT2Font *right_ft_object = glyph_to_font[right]; if (left_ft_object != right_ft_object) { - printf("Prev FT2Font != Curr FT2Font!\n"); + // could potentially do something different? } // if left_ft_object is not the same the right_ft_object, // do the exact same thing which set_text does. From 3f42cbd0465dffc97494e93f558bdce6d556f1ad Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 13:42:39 +0530 Subject: [PATCH 11/32] Windows compiler fix --- src/ft2font.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index fdabb0bd2034..654b17824f8a 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -392,7 +392,7 @@ void FT2Font::clear() glyph_to_font.clear(); char_to_font.clear(); - for (uint i = 0; i < fallbacks.size(); i ++) { + for (unsigned int i = 0; i < fallbacks.size(); i++) { fallbacks[i]->clear(); } } @@ -407,7 +407,7 @@ void FT2Font::set_size(double ptsize, double dpi) FT_Matrix transform = { 65536 / hinting_factor, 0, 0, 65536 }; FT_Set_Transform(face, &transform, 0); - for (uint i = 0; i < fallbacks.size(); i++) { + for (unsigned int i = 0; i < fallbacks.size(); i++) { fallbacks[i]->set_size(ptsize, dpi); } } @@ -477,7 +477,7 @@ int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &d void FT2Font::set_kerning_factor(int factor) { kerning_factor = factor; - for (uint i = 0; i < fallbacks.size(); i ++){ + for (unsigned int i = 0; i < fallbacks.size(); i ++){ fallbacks[i]->set_kerning_factor(factor); } } @@ -702,7 +702,7 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, } else { - for (uint i = 0; i < fallbacks.size(); ++i) { + for (unsigned int i = 0; i < fallbacks.size(); ++i) { bool was_found = fallbacks[i]->load_char_with_fallback( ft_object_with_glyph, final_glyph_index, parent_glyphs, parent_char_to_font, parent_glyph_to_font, charcode, flags, charcode_error, glyph_error, override); From 9c062cdecbd3a21b130c9e5b0322c339513c5be8 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Fri, 13 Aug 2021 16:06:11 +0530 Subject: [PATCH 12/32] Add fallback test for Agg backend --- .../baseline_images/test_agg/font_fallback.png | Bin 0 -> 19954 bytes lib/matplotlib/tests/test_agg.py | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 lib/matplotlib/tests/baseline_images/test_agg/font_fallback.png diff --git a/lib/matplotlib/tests/baseline_images/test_agg/font_fallback.png b/lib/matplotlib/tests/baseline_images/test_agg/font_fallback.png new file mode 100644 index 0000000000000000000000000000000000000000..546beec003eaf77b7767faa5a00472435a65916c GIT binary patch literal 19954 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 f|4V;cY-!U~K9_ Date: Wed, 28 Jul 2021 20:05:31 +0530 Subject: [PATCH 13/32] Debug fontNames --- lib/matplotlib/backends/backend_pdf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 9980d49af85b..fff63f7c6bec 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -907,6 +907,7 @@ def dviFontName(self, dvifont): return pdfname def writeFonts(self): + print(self.fontNames) fonts = {} for dviname, info in sorted(self.dviFontInfo.items()): Fx = info.pdfname From 9950a3eb4b3089dd336486977ce7df9c41129026 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Thu, 29 Jul 2021 21:56:18 +0530 Subject: [PATCH 14/32] Segfaults on exit --- lib/matplotlib/backends/backend_agg.py | 1 + lib/matplotlib/backends/backend_pdf.py | 29 ++++++++++++++++---------- src/ft2font.cpp | 5 +++++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index b5a2f85affc1..ddc697f61180 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -185,6 +185,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): return None # We pass '0' for angle here, since it will be rotated (in raster # space) in the following call to draw_text_image). + print("BEFORE") font.set_text(s, 0, flags=flags) font.draw_glyphs_to_bitmap( antialiased=mpl.rcParams['text.antialiased']) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index fff63f7c6bec..73f8d08a4fc9 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -33,7 +33,7 @@ GraphicsContextBase, RendererBase) from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.figure import Figure -from matplotlib.font_manager import findfont, get_font +from matplotlib.font_manager import findfont, find_fontsprop, get_font from matplotlib.afm import AFM import matplotlib.type1font as type1font import matplotlib.dviread as dviread @@ -861,20 +861,24 @@ def fontName(self, fontprop): """ if isinstance(fontprop, str): - filename = fontprop + filename = [fontprop] elif mpl.rcParams['pdf.use14corefonts']: - filename = findfont( - fontprop, fontext='afm', directory=RendererPdf._afm_font_dir) + filename = find_fontsprop( + fontprop, fontext='afm', directory=RendererPdf._afm_font_dir + ).values() else: - filename = findfont(fontprop) + filename = find_fontsprop(fontprop).values() - Fx = self.fontNames.get(filename) - if Fx is None: - Fx = next(self._internal_font_seq) - self.fontNames[filename] = Fx - _log.debug('Assigning font %s = %r', Fx, filename) + Fxs = [] + for fname in filename: + Fx = self.fontNames.get(fname) + if Fx is None: + Fx = next(self._internal_font_seq) + self.fontNames[fname] = Fx + _log.debug('Assigning font %s = %r', Fx, fname) + Fxs.append(Fx) - return Fx + return Fxs[0] def dviFontName(self, dvifont): """ @@ -1066,6 +1070,9 @@ def createType1Descriptor(self, t1font, fontfile): def _get_xobject_symbol_name(self, filename, symbol_name): Fx = self.fontName(filename) + # TODO: XObject symbol name should be multiple names? + # list(map(lambda x: x.name.decode(), Fxs)) + # Fx = Fxs[0] return "-".join([ Fx.name.decode(), os.path.splitext(os.path.basename(filename))[0], diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 654b17824f8a..8b8f9bc76c73 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -555,6 +555,11 @@ void FT2Font::set_text( if (bbox.xMin > bbox.xMax) { bbox.xMin = bbox.yMin = bbox.xMax = bbox.yMax = 0; } + printf("\nMap: \n"); + // print out num_glyphs for the final FT2Font so its easy to track + for (std::pair &x: glyph_to_font) { + printf("%u: %lu \n", x.first, x.second->get_face()->num_glyphs); + } } void FT2Font::fill_glyphs( From ab2f95ae86e74eeeb96097d6ae4fd0425ab2473e Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 31 Jul 2021 23:58:05 +0530 Subject: [PATCH 15/32] More work on PDF backend --- lib/matplotlib/_text_helpers.py | 4 +++- lib/matplotlib/backends/_backend_pdf_ps.py | 2 +- lib/matplotlib/backends/backend_pdf.py | 21 ++++++++++++++++++--- src/ft2font.cpp | 11 +++++++++++ src/ft2font_wrapper.cpp | 7 ++++++- 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/_text_helpers.py b/lib/matplotlib/_text_helpers.py index 75d84997be9f..1647eceb4fe9 100644 --- a/lib/matplotlib/_text_helpers.py +++ b/lib/matplotlib/_text_helpers.py @@ -57,12 +57,14 @@ def layout(string, font, *, kern_mode=KERNING_DEFAULT): """ x = 0 prev_glyph_idx = None + print("Inside _text_helpers.py") + # breakpoint() for char in string: glyph_idx = font.get_char_index(ord(char)) kern = (font.get_kerning(prev_glyph_idx, glyph_idx, kern_mode) / 64 if prev_glyph_idx is not None else 0.) x += kern - glyph = font.load_glyph(glyph_idx, flags=LOAD_NO_HINTING) + glyph = font.load_glyph(glyph_idx, flags=LOAD_NO_HINTING, fallback=True) yield LayoutItem(char, glyph_idx, x, kern) x += glyph.linearHoriAdvance / 65536 prev_glyph_idx = glyph_idx diff --git a/lib/matplotlib/backends/_backend_pdf_ps.py b/lib/matplotlib/backends/_backend_pdf_ps.py index 3224fb90e3a9..ef1568f142b0 100644 --- a/lib/matplotlib/backends/_backend_pdf_ps.py +++ b/lib/matplotlib/backends/_backend_pdf_ps.py @@ -136,7 +136,7 @@ def _get_font_afm(self, prop): return _cached_get_afm_from_fname(fname) def _get_font_ttf(self, prop): - fname = font_manager.findfont(prop) + fname = font_manager.find_fontsprop(prop) font = font_manager.get_font(fname) font.clear() font.set_size(prop.get_size_in_points(), 72) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 73f8d08a4fc9..7f5d2a01bece 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -692,6 +692,7 @@ def __init__(self, filename, metadata=None): # differently encoded Type-1 fonts may share the same descriptor self.type1Descriptors = {} self._character_tracker = _backend_pdf_ps.CharacterTracker() + # self._char_to_font = None self.alphaStates = {} # maps alpha values to graphics state objects self._alpha_state_seq = (Name(f'A{i}') for i in itertools.count(1)) @@ -911,7 +912,8 @@ def dviFontName(self, dvifont): return pdfname def writeFonts(self): - print(self.fontNames) + # print("OHNO") + # breakpoint() fonts = {} for dviname, info in sorted(self.dviFontInfo.items()): Fx = info.pdfname @@ -927,7 +929,15 @@ def writeFonts(self): else: # a normal TrueType font _log.debug('Writing TrueType font.') + # characters = [] + print("lalalla") + # breakpoint() + # for key, val in self._char_to_font: + # if val.fname == filename: + # print("nice", filename) + # characters.append(key) chars = self._character_tracker.used.get(filename) + print("chars:", chars, " for font:", filename) if chars: fonts[Fx] = self.embedTTF(filename, chars) self.writeObject(self.fontObject, fonts) @@ -1149,7 +1159,7 @@ def embedTTFType3(font, characters, descriptor): def get_char_width(charcode): s = ord(cp1252.decoding_table[charcode]) width = font.load_char( - s, flags=LOAD_NO_SCALE | LOAD_NO_HINTING).horiAdvance + s, fallback=False, flags=LOAD_NO_SCALE | LOAD_NO_HINTING).horiAdvance return cvt(width) with warnings.catch_warnings(): @@ -2339,7 +2349,12 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): fonttype = 1 else: font = self._get_font_ttf(prop) - self.file._character_tracker.track(font, s) + font.set_text(s) + char_to_font = font.get_char_to_font() + for char, font in char_to_font.items(): + print(chr(char), "to:", font.fname) + self.file._character_tracker.track(font, chr(char)) + print("\nFONT_TO_CHAR:", self.file._character_tracker.used) fonttype = mpl.rcParams['pdf.fonttype'] if gc.get_url() is not None: diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 8b8f9bc76c73..d3a8691d4ba9 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -735,6 +735,17 @@ void FT2Font::load_glyph(FT_UInt glyph_index, void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) { + // search cache first + if (fallback == 1 && glyph_to_font.find(glyph_index) != glyph_to_font.end()) { + ft_object = glyph_to_font[glyph_index]; + return; + } + // can not do fallback without a charcode + // so ignore exact condition fallback == 1 + + // set as self + ft_object = this; + if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { throw_ft_error("Could not load glyph", error); } diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index b08e937e3aa8..5373693db550 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -766,7 +766,8 @@ static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject * long charcode; int fallback = 1; FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; - const char *names[] = { "charcode", "flags", NULL }; + const char *names[] = { "charcode", "flags", "fallback", NULL }; + printf("Loading char!\n"); /* This makes a technically incorrect assumption that FT_Int32 is int. In theory it can also be long, if the size of int is less @@ -775,6 +776,10 @@ static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject * &flags)) { return NULL; } + + FT2Font *ft_object = NULL; + CALL_CPP("load_char", (self->x->load_char(charcode, flags, fallback, ft_object))); + printf("Char %ld loaded to: %lu\n", charcode, ft_object->get_face()->num_glyphs); FT2Font *ft_object = NULL; CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); From 41e247df446e1bde8750ead56f0bd901edf84da5 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sun, 1 Aug 2021 17:23:09 +0530 Subject: [PATCH 16/32] Implement another approach --- lib/matplotlib/_text_helpers.py | 3 +- lib/matplotlib/backends/backend_agg.py | 1 - lib/matplotlib/backends/backend_pdf.py | 38 ++++++++++++++------------ src/ft2font.cpp | 20 ++++++++++---- src/ft2font_wrapper.cpp | 12 +++++--- 5 files changed, 45 insertions(+), 29 deletions(-) diff --git a/lib/matplotlib/_text_helpers.py b/lib/matplotlib/_text_helpers.py index 1647eceb4fe9..17553b93d4a8 100644 --- a/lib/matplotlib/_text_helpers.py +++ b/lib/matplotlib/_text_helpers.py @@ -60,11 +60,12 @@ def layout(string, font, *, kern_mode=KERNING_DEFAULT): print("Inside _text_helpers.py") # breakpoint() for char in string: + print("\nchecking:", char, "\n") glyph_idx = font.get_char_index(ord(char)) kern = (font.get_kerning(prev_glyph_idx, glyph_idx, kern_mode) / 64 if prev_glyph_idx is not None else 0.) x += kern - glyph = font.load_glyph(glyph_idx, flags=LOAD_NO_HINTING, fallback=True) + glyph = font.load_glyph(glyph_idx, flags=LOAD_NO_HINTING) yield LayoutItem(char, glyph_idx, x, kern) x += glyph.linearHoriAdvance / 65536 prev_glyph_idx = glyph_idx diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index ddc697f61180..b5a2f85affc1 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -185,7 +185,6 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): return None # We pass '0' for angle here, since it will be rotated (in raster # space) in the following call to draw_text_image). - print("BEFORE") font.set_text(s, 0, flags=flags) font.draw_glyphs_to_bitmap( antialiased=mpl.rcParams['text.antialiased']) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 7f5d2a01bece..66c5ad0a5c6d 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -879,6 +879,7 @@ def fontName(self, fontprop): _log.debug('Assigning font %s = %r', Fx, fname) Fxs.append(Fx) + # return only the first for Op.selectfont to work return Fxs[0] def dviFontName(self, dvifont): @@ -912,8 +913,7 @@ def dviFontName(self, dvifont): return pdfname def writeFonts(self): - # print("OHNO") - # breakpoint() + print("fonts: ", self.fontNames) fonts = {} for dviname, info in sorted(self.dviFontInfo.items()): Fx = info.pdfname @@ -929,13 +929,6 @@ def writeFonts(self): else: # a normal TrueType font _log.debug('Writing TrueType font.') - # characters = [] - print("lalalla") - # breakpoint() - # for key, val in self._char_to_font: - # if val.fname == filename: - # print("nice", filename) - # characters.append(key) chars = self._character_tracker.used.get(filename) print("chars:", chars, " for font:", filename) if chars: @@ -1079,14 +1072,14 @@ def createType1Descriptor(self, t1font, fontfile): return fontdescObject def _get_xobject_symbol_name(self, filename, symbol_name): + # since filename is a string Fx = self.fontName(filename) - # TODO: XObject symbol name should be multiple names? - # list(map(lambda x: x.name.decode(), Fxs)) - # Fx = Fxs[0] - return "-".join([ + x = "-".join([ Fx.name.decode(), os.path.splitext(os.path.basename(filename))[0], symbol_name]) + print("\n\nXOBJECT", x, "\n\n") + return x _identityToUnicodeCMap = b"""/CIDInit /ProcSet findresource begin 12 dict begin @@ -1113,6 +1106,7 @@ def embedTTF(self, filename, characters): """Embed the TTF font from the named file into the document.""" font = get_font(filename) + print("embedding:", font.fname) fonttype = mpl.rcParams['pdf.fonttype'] def cvt(length, upe=font.units_per_EM, nearest=True): @@ -1159,9 +1153,8 @@ def embedTTFType3(font, characters, descriptor): def get_char_width(charcode): s = ord(cp1252.decoding_table[charcode]) width = font.load_char( - s, fallback=False, flags=LOAD_NO_SCALE | LOAD_NO_HINTING).horiAdvance + s, flags=LOAD_NO_SCALE | LOAD_NO_HINTING).horiAdvance return cvt(width) - with warnings.catch_warnings(): # Ignore 'Required glyph missing from current font' warning # from ft2font: here we're just building the widths table, but @@ -2419,6 +2412,11 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): -math.sin(a), math.cos(a), x, y, Op.concat_matrix) # Emit all the 1-byte characters in a BT/ET group. + + x = self.file.fontName(prop) + print(x) + # breakpoint() + self.file.output(Op.begin_text, self.file.fontName(prop), fontsize, Op.selectfont) prev_start_x = 0 @@ -2434,12 +2432,18 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): prev_start_x = start_x self.file.output(Op.end_text) # Then emit all the multibyte characters, one at a time. + glyph_to_font = font.get_glyph_to_font() for start_x, glyph_idx in multibyte_glyphs: - self._draw_xobject_glyph(font, fontsize, glyph_idx, start_x, 0) + self._draw_xobject_glyph(glyph_to_font, fontsize, glyph_idx, start_x, 0) self.file.output(Op.grestore) + # print("fine here") - def _draw_xobject_glyph(self, font, fontsize, glyph_idx, x, y): + def _draw_xobject_glyph(self, glyph_to_font, fontsize, glyph_idx, x, y): """Draw a multibyte character from a Type 3 font as an XObject.""" + if glyph_idx not in glyph_to_font: + # ideally raise. + pass + font = glyph_to_font[glyph_idx] symbol_name = font.get_glyph_name(glyph_idx) name = self.file._get_xobject_symbol_name(font.fname, symbol_name) self.file.output( diff --git a/src/ft2font.cpp b/src/ft2font.cpp index d3a8691d4ba9..8d1554b570d2 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -375,6 +375,7 @@ FT2Font::~FT2Font() } if (face) { + printf("Deleting face from: %lu\n", face->num_glyphs); FT_Done_Face(face); } } @@ -555,11 +556,11 @@ void FT2Font::set_text( if (bbox.xMin > bbox.xMax) { bbox.xMin = bbox.yMin = bbox.xMax = bbox.yMax = 0; } - printf("\nMap: \n"); - // print out num_glyphs for the final FT2Font so its easy to track - for (std::pair &x: glyph_to_font) { - printf("%u: %lu \n", x.first, x.second->get_face()->num_glyphs); - } + // printf("\nMap: \n"); + // // print out num_glyphs for the final FT2Font so its easy to track + // for (std::pair &x: glyph_to_font) { + // printf("%u: %lu \n", x.first, x.second->get_face()->num_glyphs); + // } } void FT2Font::fill_glyphs( @@ -685,6 +686,7 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, bool override = false) { FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); + // printf("fallback glyph id: %u\n", glyph_index); if (glyph_index || override) { charcode_error = FT_Load_Glyph(face, glyph_index, flags); @@ -736,7 +738,8 @@ void FT2Font::load_glyph(FT_UInt glyph_index, void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) { // search cache first - if (fallback == 1 && glyph_to_font.find(glyph_index) != glyph_to_font.end()) { + if (fallback && glyph_to_font.find(glyph_index) != glyph_to_font.end()) { + // printf("load_glyph: Already present in cache.\n"); ft_object = glyph_to_font[glyph_index]; return; } @@ -778,6 +781,11 @@ void FT2Font::get_cbox(FT_BBox &bbox) FT_Glyph_Get_CBox(glyphs.back(), ft_glyph_bbox_subpixels, &bbox); } +void FT2Font::get_cbox(FT_BBox &bbox) +{ + FT_Glyph_Get_CBox(glyphs.back(), ft_glyph_bbox_subpixels, &bbox); +} + void FT2Font::get_width_height(long *width, long *height) { *width = advance; diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 5373693db550..c045f77953ee 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -766,8 +766,7 @@ static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject * long charcode; int fallback = 1; FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; - const char *names[] = { "charcode", "flags", "fallback", NULL }; - printf("Loading char!\n"); + const char *names[] = { "charcode", "flags", NULL }; /* This makes a technically incorrect assumption that FT_Int32 is int. In theory it can also be long, if the size of int is less @@ -776,9 +775,9 @@ static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject * &flags)) { return NULL; } - + FT2Font *ft_object = NULL; - CALL_CPP("load_char", (self->x->load_char(charcode, flags, fallback, ft_object))); + CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); printf("Char %ld loaded to: %lu\n", charcode, ft_object->get_face()->num_glyphs); FT2Font *ft_object = NULL; @@ -819,6 +818,11 @@ static PyObject *PyFT2Font_load_glyph(PyFT2Font *self, PyObject *args, PyObject return NULL; } + // if (!PyArg_ParseTupleAndKeywords( + // args, kwds, "I|i$p:load_glyph", (char **)names, &glyph_index, &flags, &fallback)) { + // return NULL; + // } + FT2Font *ft_object = NULL; CALL_CPP("load_glyph", (self->x->load_glyph(glyph_index, flags, ft_object, (bool)fallback))); From e5b4daed15fa6d83ec746692c88cfcc12579ec56 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Tue, 3 Aug 2021 19:23:34 +0530 Subject: [PATCH 17/32] Revisit the approach --- lib/matplotlib/_text_helpers.py | 7 +-- lib/matplotlib/backends/backend_pdf.py | 73 ++++++++++++-------------- src/ft2font_wrapper.cpp | 2 +- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/lib/matplotlib/_text_helpers.py b/lib/matplotlib/_text_helpers.py index 17553b93d4a8..5c44009ae5f7 100644 --- a/lib/matplotlib/_text_helpers.py +++ b/lib/matplotlib/_text_helpers.py @@ -9,7 +9,7 @@ LayoutItem = dataclasses.make_dataclass( - "LayoutItem", ["char", "glyph_idx", "x", "prev_kern"]) + "LayoutItem", ["ft_object", "char", "glyph_idx", "x", "prev_kern"]) def warn_on_missing_glyph(codepoint): @@ -60,12 +60,13 @@ def layout(string, font, *, kern_mode=KERNING_DEFAULT): print("Inside _text_helpers.py") # breakpoint() for char in string: - print("\nchecking:", char, "\n") + # print("\nchecking:", char, "\n") glyph_idx = font.get_char_index(ord(char)) kern = (font.get_kerning(prev_glyph_idx, glyph_idx, kern_mode) / 64 if prev_glyph_idx is not None else 0.) x += kern glyph = font.load_glyph(glyph_idx, flags=LOAD_NO_HINTING) - yield LayoutItem(char, glyph_idx, x, kern) + ft_object = font.get_glyph_to_font().get(glyph_idx) + yield LayoutItem(ft_object, char, glyph_idx, x, kern) x += glyph.linearHoriAdvance / 65536 prev_glyph_idx = glyph_idx diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 66c5ad0a5c6d..ae1bf4b418db 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -862,25 +862,21 @@ def fontName(self, fontprop): """ if isinstance(fontprop, str): - filename = [fontprop] + filename = fontprop elif mpl.rcParams['pdf.use14corefonts']: - filename = find_fontsprop( + filename = findfont( fontprop, fontext='afm', directory=RendererPdf._afm_font_dir - ).values() + ) else: - filename = find_fontsprop(fontprop).values() + filename = findfont(fontprop) - Fxs = [] - for fname in filename: - Fx = self.fontNames.get(fname) - if Fx is None: - Fx = next(self._internal_font_seq) - self.fontNames[fname] = Fx - _log.debug('Assigning font %s = %r', Fx, fname) - Fxs.append(Fx) + Fx = self.fontNames.get(filename) + if Fx is None: + Fx = next(self._internal_font_seq) + self.fontNames[filename] = Fx + _log.debug('Assigning font %s = %r', Fx, filename) - # return only the first for Op.selectfont to work - return Fxs[0] + return Fx def dviFontName(self, dvifont): """ @@ -1072,7 +1068,6 @@ def createType1Descriptor(self, t1font, fontfile): return fontdescObject def _get_xobject_symbol_name(self, filename, symbol_name): - # since filename is a string Fx = self.fontName(filename) x = "-".join([ Fx.name.decode(), @@ -1924,6 +1919,8 @@ class RendererPdf(_backend_pdf_ps.RendererPDFPSBase): def __init__(self, file, image_dpi, height, width): super().__init__(width, height) self.file = file + self.char_to_font = {} + self.glyph_to_font = {} self.gc = self.new_gc() self.image_dpi = image_dpi @@ -2343,8 +2340,11 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): else: font = self._get_font_ttf(prop) font.set_text(s) - char_to_font = font.get_char_to_font() - for char, font in char_to_font.items(): + self.char_to_font = font.get_char_to_font() + self.glyph_to_font = font.get_glyph_to_font() + # populate self.fontNames with all fonts + _ = [self.file.fontName(ft_object.fname) for ft_object in self.glyph_to_font.values()] + for char, font in self.char_to_font.items(): print(chr(char), "to:", font.fname) self.file._character_tracker.track(font, chr(char)) print("\nFONT_TO_CHAR:", self.file._character_tracker.used) @@ -2387,22 +2387,22 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # the regular text show command (TJ) with appropriate kerning between # chunks, whereas multibyte characters use the XObject command (Do). else: - # List of (start_x, [prev_kern, char, char, ...]), w/o zero kerns. + # List of (ft_object, start_x, [prev_kern, char, char, ...]), w/o zero kerns. singlebyte_chunks = [] - # List of (start_x, glyph_index). + # List of (ft_object, start_x, glyph_index). multibyte_glyphs = [] prev_was_multibyte = True for item in _text_helpers.layout( s, font, kern_mode=KERNING_UNFITTED): if _font_supports_glyph(fonttype, ord(item.char)): if prev_was_multibyte: - singlebyte_chunks.append((item.x, [])) + singlebyte_chunks.append((item.ft_object, item.x, [])) if item.prev_kern: - singlebyte_chunks[-1][1].append(item.prev_kern) - singlebyte_chunks[-1][1].append(item.char) + singlebyte_chunks[-1][2].append(item.prev_kern) + singlebyte_chunks[-1][2].append(item.char) prev_was_multibyte = False else: - multibyte_glyphs.append((item.x, item.glyph_idx)) + multibyte_glyphs.append((item.ft_object, item.x, item.glyph_idx)) prev_was_multibyte = True # Do the rotation and global translation as a single matrix # concatenation up front @@ -2413,14 +2413,12 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): x, y, Op.concat_matrix) # Emit all the 1-byte characters in a BT/ET group. - x = self.file.fontName(prop) - print(x) - # breakpoint() - - self.file.output(Op.begin_text, - self.file.fontName(prop), fontsize, Op.selectfont) + self.file.output(Op.begin_text) + # ft, fontsize, Op.selectfont prev_start_x = 0 - for start_x, kerns_or_chars in singlebyte_chunks: + for ft_object, start_x, kerns_or_chars in singlebyte_chunks: + ft_name = self.file.fontName(ft_object.fname) + self.file.output(ft_name, fontsize, Op.selectfont) self._setup_textpos(start_x, 0, 0, prev_start_x, 0, 0) self.file.output( # See pdf spec "Text space details" for the 1000/fontsize @@ -2432,20 +2430,15 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): prev_start_x = start_x self.file.output(Op.end_text) # Then emit all the multibyte characters, one at a time. - glyph_to_font = font.get_glyph_to_font() - for start_x, glyph_idx in multibyte_glyphs: - self._draw_xobject_glyph(glyph_to_font, fontsize, glyph_idx, start_x, 0) + for ft_object, start_x, glyph_idx in multibyte_glyphs: + self._draw_xobject_glyph(ft_object, fontsize, glyph_idx, start_x, 0) self.file.output(Op.grestore) # print("fine here") - def _draw_xobject_glyph(self, glyph_to_font, fontsize, glyph_idx, x, y): + def _draw_xobject_glyph(self, ft_object, fontsize, glyph_idx, x, y): """Draw a multibyte character from a Type 3 font as an XObject.""" - if glyph_idx not in glyph_to_font: - # ideally raise. - pass - font = glyph_to_font[glyph_idx] - symbol_name = font.get_glyph_name(glyph_idx) - name = self.file._get_xobject_symbol_name(font.fname, symbol_name) + symbol_name = ft_object.get_glyph_name(glyph_idx) + name = self.file._get_xobject_symbol_name(ft_object.fname, symbol_name) self.file.output( Op.gsave, 0.001 * fontsize, 0, 0, 0.001 * fontsize, x, y, Op.concat_matrix, diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index c045f77953ee..118a7c899cf4 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -778,7 +778,7 @@ static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject * FT2Font *ft_object = NULL; CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); - printf("Char %ld loaded to: %lu\n", charcode, ft_object->get_face()->num_glyphs); + // printf("Char %ld loaded to: %lu\n", charcode, ft_object->get_face()->num_glyphs); FT2Font *ft_object = NULL; CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); From 3af91d8926e896a0c73b64b6d8abec1ddfdbbbc7 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Fri, 6 Aug 2021 09:17:19 +0530 Subject: [PATCH 18/32] Type3 PDF Backend works! --- lib/matplotlib/_text_helpers.py | 5 +--- lib/matplotlib/backends/backend_pdf.py | 34 +++++++++++++++----------- lib/matplotlib/font_manager.py | 1 + src/ft2font.cpp | 16 ++++++------ 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/lib/matplotlib/_text_helpers.py b/lib/matplotlib/_text_helpers.py index 5c44009ae5f7..ca95c8081c47 100644 --- a/lib/matplotlib/_text_helpers.py +++ b/lib/matplotlib/_text_helpers.py @@ -57,16 +57,13 @@ def layout(string, font, *, kern_mode=KERNING_DEFAULT): """ x = 0 prev_glyph_idx = None - print("Inside _text_helpers.py") - # breakpoint() for char in string: - # print("\nchecking:", char, "\n") glyph_idx = font.get_char_index(ord(char)) kern = (font.get_kerning(prev_glyph_idx, glyph_idx, kern_mode) / 64 if prev_glyph_idx is not None else 0.) x += kern glyph = font.load_glyph(glyph_idx, flags=LOAD_NO_HINTING) - ft_object = font.get_glyph_to_font().get(glyph_idx) + ft_object = font.get_glyph_to_font().get(glyph_idx, font) yield LayoutItem(ft_object, char, glyph_idx, x, kern) x += glyph.linearHoriAdvance / 65536 prev_glyph_idx = glyph_idx diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index ae1bf4b418db..57dae0bbcaf1 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -862,21 +862,28 @@ def fontName(self, fontprop): """ if isinstance(fontprop, str): - filename = fontprop + filenames = [fontprop] elif mpl.rcParams['pdf.use14corefonts']: - filename = findfont( + filenames = find_fontsprop( fontprop, fontext='afm', directory=RendererPdf._afm_font_dir - ) + ).values() else: - filename = findfont(fontprop) - - Fx = self.fontNames.get(filename) - if Fx is None: - Fx = next(self._internal_font_seq) - self.fontNames[filename] = Fx - _log.debug('Assigning font %s = %r', Fx, filename) - - return Fx + filenames = find_fontsprop(fontprop).values() + first_Fx = None + for fname in filenames: + Fx = self.fontNames.get(fname) + if not first_Fx: + first_Fx = Fx + if Fx is None: + Fx = next(self._internal_font_seq) + self.fontNames[fname] = Fx + _log.debug('Assigning font %s = %r', Fx, fname) + if not first_Fx: + first_Fx = Fx + + # find_fontsprop's first value always adheres to + # findfont's value, so technically no behaviour change + return first_Fx def dviFontName(self, dvifont): """ @@ -1073,7 +1080,6 @@ def _get_xobject_symbol_name(self, filename, symbol_name): Fx.name.decode(), os.path.splitext(os.path.basename(filename))[0], symbol_name]) - print("\n\nXOBJECT", x, "\n\n") return x _identityToUnicodeCMap = b"""/CIDInit /ProcSet findresource begin @@ -2345,7 +2351,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # populate self.fontNames with all fonts _ = [self.file.fontName(ft_object.fname) for ft_object in self.glyph_to_font.values()] for char, font in self.char_to_font.items(): - print(chr(char), "to:", font.fname) + # print(chr(char), "to:", font.fname) self.file._character_tracker.track(font, chr(char)) print("\nFONT_TO_CHAR:", self.file._character_tracker.used) fonttype = mpl.rcParams['pdf.fonttype'] diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index aca91f25d7e4..6c88622deecc 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -1355,6 +1355,7 @@ def find_fontsprop(self, prop, fontext='ttf', directory=None, See `findfont` for more details. """ + # print("finding font!") rc_params = tuple(tuple(rcParams[key]) for key in [ "font.serif", "font.sans-serif", "font.cursive", "font.fantasy", diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 8d1554b570d2..c74def377d85 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -557,7 +557,7 @@ void FT2Font::set_text( bbox.xMin = bbox.yMin = bbox.xMax = bbox.yMax = 0; } // printf("\nMap: \n"); - // // print out num_glyphs for the final FT2Font so its easy to track + // print out num_glyphs for the final FT2Font so its easy to track // for (std::pair &x: glyph_to_font) { // printf("%u: %lu \n", x.first, x.second->get_face()->num_glyphs); // } @@ -692,7 +692,6 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, charcode_error = FT_Load_Glyph(face, glyph_index, flags); FT_Glyph thisGlyph; glyph_error = FT_Get_Glyph(face->glyph, &thisGlyph); - if (charcode_error || glyph_error) { return false; } @@ -739,16 +738,17 @@ void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) { // search cache first if (fallback && glyph_to_font.find(glyph_index) != glyph_to_font.end()) { - // printf("load_glyph: Already present in cache.\n"); + printf("load_glyph: Already present in cache.\n"); ft_object = glyph_to_font[glyph_index]; - return; + } else { + ft_object = this; } - // can not do fallback without a charcode - // so ignore exact condition fallback == 1 - // set as self - ft_object = this; + ft_object->load_glyph(glyph_index, flags); +} +void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) +{ if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { throw_ft_error("Could not load glyph", error); } From 7ee77f2971c3047fe6b85a3dda5751f2606cee58 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 05:08:18 +0530 Subject: [PATCH 19/32] Type42 PDF fallback works! --- lib/matplotlib/backends/backend_pdf.py | 3 ++- src/ft2font.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 57dae0bbcaf1..5273b5b586dc 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -2398,11 +2398,13 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # List of (ft_object, start_x, glyph_index). multibyte_glyphs = [] prev_was_multibyte = True + prev_font = font for item in _text_helpers.layout( s, font, kern_mode=KERNING_UNFITTED): if _font_supports_glyph(fonttype, ord(item.char)): if prev_was_multibyte: singlebyte_chunks.append((item.ft_object, item.x, [])) + prev_font = item.ft_object if item.prev_kern: singlebyte_chunks[-1][2].append(item.prev_kern) singlebyte_chunks[-1][2].append(item.char) @@ -2439,7 +2441,6 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): for ft_object, start_x, glyph_idx in multibyte_glyphs: self._draw_xobject_glyph(ft_object, fontsize, glyph_idx, start_x, 0) self.file.output(Op.grestore) - # print("fine here") def _draw_xobject_glyph(self, ft_object, fontsize, glyph_idx, x, y): """Draw a multibyte character from a Type 3 font as an XObject.""" diff --git a/src/ft2font.cpp b/src/ft2font.cpp index c74def377d85..a0f84cf11700 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -738,7 +738,7 @@ void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) { // search cache first if (fallback && glyph_to_font.find(glyph_index) != glyph_to_font.end()) { - printf("load_glyph: Already present in cache.\n"); + // printf("load_glyph: Already present in cache.\n"); ft_object = glyph_to_font[glyph_index]; } else { ft_object = this; From 3cacd454a19010ca6a840f2f2db59db1446a2821 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 06:14:33 +0530 Subject: [PATCH 20/32] Create a fill_glyphs method --- src/ft2font.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index a0f84cf11700..29ec2b1b0033 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -556,11 +556,6 @@ void FT2Font::set_text( if (bbox.xMin > bbox.xMax) { bbox.xMin = bbox.yMin = bbox.xMax = bbox.yMax = 0; } - // printf("\nMap: \n"); - // print out num_glyphs for the final FT2Font so its easy to track - // for (std::pair &x: glyph_to_font) { - // printf("%u: %lu \n", x.first, x.second->get_face()->num_glyphs); - // } } void FT2Font::fill_glyphs( @@ -686,7 +681,6 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, bool override = false) { FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); - // printf("fallback glyph id: %u\n", glyph_index); if (glyph_index || override) { charcode_error = FT_Load_Glyph(face, glyph_index, flags); @@ -738,7 +732,6 @@ void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) { // search cache first if (fallback && glyph_to_font.find(glyph_index) != glyph_to_font.end()) { - // printf("load_glyph: Already present in cache.\n"); ft_object = glyph_to_font[glyph_index]; } else { ft_object = this; From 8719e2ee78d8f997c3bbe3bf12986bdc7234973c Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 06:14:49 +0530 Subject: [PATCH 21/32] Use fill_glyphs instead of set_text --- lib/matplotlib/backends/backend_pdf.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 5273b5b586dc..2a5c72e6f996 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -2345,15 +2345,9 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): fonttype = 1 else: font = self._get_font_ttf(prop) - font.set_text(s) - self.char_to_font = font.get_char_to_font() - self.glyph_to_font = font.get_glyph_to_font() - # populate self.fontNames with all fonts - _ = [self.file.fontName(ft_object.fname) for ft_object in self.glyph_to_font.values()] - for char, font in self.char_to_font.items(): - # print(chr(char), "to:", font.fname) + char_to_font = font.fill_glyphs(s) + for char, font in char_to_font.items(): self.file._character_tracker.track(font, chr(char)) - print("\nFONT_TO_CHAR:", self.file._character_tracker.used) fonttype = mpl.rcParams['pdf.fonttype'] if gc.get_url() is not None: From e6c7998d9515fc3fb621ac6c98a11c2f08944133 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 06:17:15 +0530 Subject: [PATCH 22/32] Cleanup wrapper --- src/ft2font_wrapper.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 118a7c899cf4..d504eae7c041 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -778,7 +778,6 @@ static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject * FT2Font *ft_object = NULL; CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); - // printf("Char %ld loaded to: %lu\n", charcode, ft_object->get_face()->num_glyphs); FT2Font *ft_object = NULL; CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); @@ -818,11 +817,6 @@ static PyObject *PyFT2Font_load_glyph(PyFT2Font *self, PyObject *args, PyObject return NULL; } - // if (!PyArg_ParseTupleAndKeywords( - // args, kwds, "I|i$p:load_glyph", (char **)names, &glyph_index, &flags, &fallback)) { - // return NULL; - // } - FT2Font *ft_object = NULL; CALL_CPP("load_glyph", (self->x->load_glyph(glyph_index, flags, ft_object, (bool)fallback))); From f86ec417c2cdeed13d86d14c2fb6d050429f0437 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 06:54:30 +0530 Subject: [PATCH 23/32] Few cleanups --- lib/matplotlib/backends/backend_pdf.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 2a5c72e6f996..fac289fce37e 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -692,7 +692,6 @@ def __init__(self, filename, metadata=None): # differently encoded Type-1 fonts may share the same descriptor self.type1Descriptors = {} self._character_tracker = _backend_pdf_ps.CharacterTracker() - # self._char_to_font = None self.alphaStates = {} # maps alpha values to graphics state objects self._alpha_state_seq = (Name(f'A{i}') for i in itertools.count(1)) @@ -916,7 +915,6 @@ def dviFontName(self, dvifont): return pdfname def writeFonts(self): - print("fonts: ", self.fontNames) fonts = {} for dviname, info in sorted(self.dviFontInfo.items()): Fx = info.pdfname @@ -933,7 +931,6 @@ def writeFonts(self): # a normal TrueType font _log.debug('Writing TrueType font.') chars = self._character_tracker.used.get(filename) - print("chars:", chars, " for font:", filename) if chars: fonts[Fx] = self.embedTTF(filename, chars) self.writeObject(self.fontObject, fonts) @@ -1076,11 +1073,10 @@ def createType1Descriptor(self, t1font, fontfile): def _get_xobject_symbol_name(self, filename, symbol_name): Fx = self.fontName(filename) - x = "-".join([ + return "-".join([ Fx.name.decode(), os.path.splitext(os.path.basename(filename))[0], symbol_name]) - return x _identityToUnicodeCMap = b"""/CIDInit /ProcSet findresource begin 12 dict begin @@ -1107,7 +1103,6 @@ def embedTTF(self, filename, characters): """Embed the TTF font from the named file into the document.""" font = get_font(filename) - print("embedding:", font.fname) fonttype = mpl.rcParams['pdf.fonttype'] def cvt(length, upe=font.units_per_EM, nearest=True): @@ -1925,8 +1920,6 @@ class RendererPdf(_backend_pdf_ps.RendererPDFPSBase): def __init__(self, file, image_dpi, height, width): super().__init__(width, height) self.file = file - self.char_to_font = {} - self.glyph_to_font = {} self.gc = self.new_gc() self.image_dpi = image_dpi @@ -2416,7 +2409,6 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # Emit all the 1-byte characters in a BT/ET group. self.file.output(Op.begin_text) - # ft, fontsize, Op.selectfont prev_start_x = 0 for ft_object, start_x, kerns_or_chars in singlebyte_chunks: ft_name = self.file.fontName(ft_object.fname) From a6d21cd233e1b93f95507b07ecf87115832cf6bb Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Sat, 7 Aug 2021 07:19:52 +0530 Subject: [PATCH 24/32] Rebase from Agg backend --- src/ft2font.cpp | 18 ------------------ src/ft2font_wrapper.cpp | 3 --- 2 files changed, 21 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 29ec2b1b0033..0da6a52df9bf 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -375,7 +375,6 @@ FT2Font::~FT2Font() } if (face) { - printf("Deleting face from: %lu\n", face->num_glyphs); FT_Done_Face(face); } } @@ -728,18 +727,6 @@ void FT2Font::load_glyph(FT_UInt glyph_index, ft_object->load_glyph(glyph_index, flags); } -void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) -{ - // search cache first - if (fallback && glyph_to_font.find(glyph_index) != glyph_to_font.end()) { - ft_object = glyph_to_font[glyph_index]; - } else { - ft_object = this; - } - - ft_object->load_glyph(glyph_index, flags); -} - void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) { if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { @@ -774,11 +761,6 @@ void FT2Font::get_cbox(FT_BBox &bbox) FT_Glyph_Get_CBox(glyphs.back(), ft_glyph_bbox_subpixels, &bbox); } -void FT2Font::get_cbox(FT_BBox &bbox) -{ - FT_Glyph_Get_CBox(glyphs.back(), ft_glyph_bbox_subpixels, &bbox); -} - void FT2Font::get_width_height(long *width, long *height) { *width = advance; diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index d504eae7c041..b08e937e3aa8 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -779,9 +779,6 @@ static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject * FT2Font *ft_object = NULL; CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); - FT2Font *ft_object = NULL; - CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); - return PyGlyph_new(self->x, ft_object); } From ecf055692ed0ca6da363019ca9e2c8add6b435d7 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Fri, 13 Aug 2021 17:53:21 +0530 Subject: [PATCH 25/32] Use multi-font output for PS backend --- lib/matplotlib/backends/backend_ps.py | 43 +++++++++++++++++++-------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index f13e114a815b..9ef87cc2b373 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -640,10 +640,12 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): if ismath: return self.draw_mathtext(gc, x, y, s, prop, angle) + self.set_color(*gc.get_rgb()) + if mpl.rcParams['ps.useafm']: font = self._get_font_afm(prop) scale = 0.001 * prop.get_size_in_points() - + stream = [] thisx = 0 last_name = None # kerns returns 0 for None. xs_names = [] @@ -659,21 +661,36 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): thisx += kern * scale xs_names.append((thisx, name)) thisx += width * scale + ps_name = (font.postscript_name + .encode("ascii", "replace").decode("ascii")) + stream.append((ps_name, xs_names)) else: font = self._get_font_ttf(prop) - font.set_text(s, 0, flags=LOAD_NO_HINTING) - self._character_tracker.track(font, s) - xs_names = [(item.x, font.get_glyph_name(item.glyph_idx)) - for item in _text_helpers.layout(s, font)] - - self.set_color(*gc.get_rgb()) - ps_name = (font.postscript_name - .encode("ascii", "replace").decode("ascii")) - self.set_font(ps_name, prop.get_size_in_points()) - thetext = "\n".join(f"{x:f} 0 m /{name:s} glyphshow" - for x, name in xs_names) - self._pswriter.write(f"""\ + char_to_font = font.fill_glyphs(s, 0, flags=LOAD_NO_HINTING) + for char, font in char_to_font.items(): + self._character_tracker.track(font, chr(char)) + stream = [] + prev_font = curr_stream = None + for item in _text_helpers.layout(s, font): + ps_name = (item.ft_object.postscript_name + .encode("ascii", "replace").decode("ascii")) + if item.ft_object is not prev_font: + if curr_stream: + stream.append(curr_stream) + prev_font = item.ft_object + curr_stream = [ps_name, []] + curr_stream[1].append( + (item.x, item.ft_object.get_glyph_name(item.glyph_idx)) + ) + # append the last entry + stream.append(curr_stream) + + for ps_name, xs_names in stream: + self.set_font(ps_name, prop.get_size_in_points(), False) + thetext = "\n".join(f"{x:f} 0 m /{name:s} glyphshow" + for x, name in xs_names) + self._pswriter.write(f"""\ gsave {self._get_clip_cmd(gc)} {x:f} {y:f} translate From b55f011bd6a728d195b31bfa32a5027fe4bd7d48 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Fri, 13 Aug 2021 17:54:12 +0530 Subject: [PATCH 26/32] Add multi-font tests for PS backend --- lib/matplotlib/tests/test_backend_ps.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/matplotlib/tests/test_backend_ps.py b/lib/matplotlib/tests/test_backend_ps.py index 74776fd048a6..50cdf03a352e 100644 --- a/lib/matplotlib/tests/test_backend_ps.py +++ b/lib/matplotlib/tests/test_backend_ps.py @@ -234,3 +234,21 @@ def test_linedash(): fig.savefig(buf, format="ps") assert buf.tell() > 0 + + +@image_comparison(["multi_font_type3.eps"]) +def test_multi_font_type3(): + plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + plt.rc('ps', fonttype=3) + + fig = plt.figure() + fig.text(0.15, 0.475, "There are 多个汉字 in between!") + + +@image_comparison(["multi_font_type42.eps"]) +def test_multi_font_type42(): + plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + plt.rc('ps', fonttype=42) + + fig = plt.figure() + fig.text(0.15, 0.475, "There are 多个汉字 in between!") From 83b227da5e2442c222aaee0b49f2d47f64ea3acd Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Fri, 13 Aug 2021 17:54:22 +0530 Subject: [PATCH 27/32] Add baseline images --- .../test_backend_ps/multi_font_type3.eps | 533 ++++++++++++++++++ .../test_backend_ps/multi_font_type42.eps | 363 ++++++++++++ 2 files changed, 896 insertions(+) create mode 100644 lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps create mode 100644 lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps diff --git a/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps new file mode 100644 index 000000000000..b94c4d56fc3a --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps @@ -0,0 +1,533 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Title: multi_font_type3.eps +%%Creator: Matplotlib v3.4.2.post1635+gbd11b22ee3.d20210813, https://matplotlib.org/ +%%CreationDate: Fri Aug 13 16:50:31 2021 +%%Orientation: portrait +%%BoundingBox: 18 180 594 612 +%%HiResBoundingBox: 18.000000 180.000000 594.000000 612.000000 +%%EndComments +%%BeginProlog +/mpldict 12 dict def +mpldict begin +/_d { bind def } bind def +/m { moveto } _d +/l { lineto } _d +/r { rlineto } _d +/c { curveto } _d +/cl { closepath } _d +/ce { closepath eofill } _d +/box { + m + 1 index 0 r + 0 exch r + neg 0 r + cl + } _d +/clipbox { + box + clip + newpath + } _d +/sc { setcachedevice } _d +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-2090 -948 3673 2524] def +/FontType 3 def +/Encoding [/space /exclam /b /a /e /h /i /n /r /t /T /w] def +/CharStrings 13 dict dup begin +/.notdef 0 def +/space{651 0 0 0 0 0 sc +ce} _d +/exclam{821 0 309 0 512 1493 sc +309 254 m +512 254 l +512 0 l +309 0 l +309 254 l + +309 1493 m +512 1493 l +512 838 l +492 481 l +330 481 l +309 838 l +309 1493 l + +ce} _d +/b{1300 0 186 -29 1188 1556 sc +997 559 m +997 694 969 800 913 877 c +858 954 781 993 684 993 c +587 993 510 954 454 877 c +399 800 371 694 371 559 c +371 424 399 317 454 240 c +510 163 587 125 684 125 c +781 125 858 163 913 240 c +969 317 997 424 997 559 c + +371 950 m +410 1017 458 1066 517 1098 c +576 1131 647 1147 729 1147 c +865 1147 975 1093 1060 985 c +1145 877 1188 735 1188 559 c +1188 383 1145 241 1060 133 c +975 25 865 -29 729 -29 c +647 -29 576 -13 517 19 c +458 52 410 101 371 168 c +371 0 l +186 0 l +186 1556 l +371 1556 l +371 950 l + +ce} _d +/a{1255 0 123 -29 1069 1147 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +ce} _d +/e{1260 0 113 -29 1151 1147 sc +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +ce} _d +/h{1298 0 186 0 1124 1556 sc +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1556 l +371 1556 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +ce} _d +/i{569 0 193 0 377 1556 sc +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +193 1556 m +377 1556 l +377 1323 l +193 1323 l +193 1556 l + +ce} _d +/n{1298 0 186 0 1124 1147 sc +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +ce} _d +/r{842 0 186 0 842 1147 sc +842 948 m +821 960 799 969 774 974 c +750 980 723 983 694 983 c +590 983 510 949 454 881 c +399 814 371 717 371 590 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +410 1014 460 1064 522 1097 c +584 1130 659 1147 748 1147 c +761 1147 775 1146 790 1144 c +805 1143 822 1140 841 1137 c +842 948 l + +ce} _d +/t{803 0 55 0 754 1438 sc +375 1438 m +375 1120 l +754 1120 l +754 977 l +375 977 l +375 369 l +375 278 387 219 412 193 c +437 167 488 154 565 154 c +754 154 l +754 0 l +565 0 l +423 0 325 26 271 79 c +217 132 190 229 190 369 c +190 977 l +55 977 l +55 1120 l +190 1120 l +190 1438 l +375 1438 l + +ce} _d +/T{1251 0 -6 0 1257 1493 sc +-6 1493 m +1257 1493 l +1257 1323 l +727 1323 l +727 0 l +524 0 l +524 1323 l +-6 1323 l +-6 1493 l + +ce} _d +/w{1675 0 86 0 1589 1120 sc +86 1120 m +270 1120 l +500 246 l +729 1120 l +946 1120 l +1176 246 l +1405 1120 l +1589 1120 l +1296 0 l +1079 0 l +838 918 l +596 0 l +379 0 l +86 1120 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /WenQuanYiZenHei def +/PaintType 0 def +/FontMatrix [0.0009765625 0 0 0.0009765625 0 0] def +/FontBBox [-129 -304 1076 986] def +/FontType 3 def +/Encoding [/uni6C49 /uni4E2A /uni591A /uni5B57] def +/CharStrings 5 dict dup begin +/.notdef 0 def +/uni6C49{1024 0 17 -119 990 820 sc +612 211 m +536 349 488 512 468 699 c +447 699 426 698 405 697 c +407 719 407 741 405 763 c +445 761 485 760 526 760 c +871 760 l +851 534 793 348 696 202 c +769 91 867 2 990 -65 c +963 -76 942 -94 928 -119 c +819 -56 727 31 653 143 c +561 27 446 -58 307 -112 c +289 -89 268 -69 243 -53 c +392 -2 515 86 612 211 c + +535 700 m +552 534 592 391 655 271 c +735 396 782 539 796 700 c +535 700 l + +151 -118 m +123 -102 88 -93 47 -92 c +76 -38 107 24 138 93 c +169 162 215 283 274 454 c +315 433 l +199 54 l +151 -118 l + +230 457 m +166 408 l +17 544 l +80 594 l +230 457 l + +248 626 m +202 677 152 724 97 768 c +157 820 l +214 773 268 723 317 670 c +248 626 l + +ce} _d +/uni4E2A{1024 0 14 -123 980 833 sc +547 -123 m +520 -120 492 -120 464 -123 c +467 -72 468 -21 468 30 c +468 362 l +468 413 467 465 464 516 c +492 513 520 513 547 516 c +545 465 544 413 544 362 c +544 30 l +544 -21 545 -72 547 -123 c + +980 427 m +955 410 939 387 931 358 c +846 384 767 429 695 494 c +624 559 563 631 514 711 c +383 520 236 378 71 285 c +59 314 40 337 14 354 c +113 405 204 471 285 550 c +367 630 433 724 484 833 c +499 822 515 813 531 805 c +537 808 l +542 800 l +549 796 557 792 564 789 c +555 775 l +614 672 682 590 759 531 c +824 484 898 450 980 427 c + +ce} _d +/uni591A{1024 0 60 -133 928 834 sc +581 89 m +526 34 l +399 161 l +319 111 239 75 158 52 c +154 81 142 107 121 128 c +260 164 367 214 444 279 c +493 322 539 367 582 416 c +601 395 623 376 646 359 c +606 321 l +928 321 l +845 176 731 63 584 -18 c +513 -58 434 -87 348 -105 c +262 -124 174 -129 85 -120 c +82 -91 73 -64 60 -39 c +205 -66 343 -51 475 6 c +608 63 713 150 792 266 c +544 266 l +518 245 492 225 465 206 c +581 89 l + +499 513 m +441 460 l +332 581 l +267 529 201 490 132 464 c +125 493 111 517 88 536 c +210 579 301 634 360 701 c +395 743 428 787 457 834 c +480 816 504 801 530 788 c +516 769 502 751 487 734 c +824 734 l +749 610 649 507 526 424 c +403 342 268 291 119 271 c +109 298 94 322 75 344 c +199 351 314 384 421 443 c +528 503 616 582 686 679 c +438 679 l +423 662 407 647 392 632 c +499 513 l + +ce} _d +/uni5B57{1024 0 32 -132 982 872 sc +982 285 m +980 264 980 243 982 222 c +943 224 904 225 865 225 c +555 225 l +555 -29 l +555 -54 545 -76 525 -95 c +496 -120 444 -132 368 -131 c +376 -98 368 -68 344 -43 c +366 -46 392 -47 422 -47 c +452 -48 470 -46 475 -42 c +480 -38 483 -27 483 -9 c +483 225 l +148 225 l +109 225 71 224 32 222 c +34 243 34 264 32 285 c +71 283 109 282 148 282 c +483 282 l +483 355 l +648 506 l +317 506 l +278 506 239 505 200 503 c +203 524 203 545 200 566 c +239 564 278 563 317 563 c +761 563 l +769 498 l +748 493 730 483 714 469 c +555 323 l +555 282 l +865 282 l +904 282 943 283 982 285 c + +131 562 m +59 562 l +59 753 l +468 752 l +390 807 l +435 872 l +542 798 l +510 752 l +925 752 l +925 562 l +852 562 l +852 695 l +131 695 l +131 562 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +end +%%EndProlog +mpldict begin +18 180 translate +576 432 0 0 clipbox +gsave +0 0 m +576 0 l +576 432 l +0 432 l +cl +1.000 setgray +fill +grestore +0.000 setgray +/DejaVuSans 27.000 selectfont +gsave + +86.400000 205.200000 translate +0.000000 rotate +0.000000 0 m /T glyphshow +16.492676 0 m /h glyphshow +33.604980 0 m /e glyphshow +50.216309 0 m /r glyphshow +61.316895 0 m /e glyphshow +77.928223 0 m /space glyphshow +86.510742 0 m /a glyphshow +103.056152 0 m /r glyphshow +114.156738 0 m /e glyphshow +130.768066 0 m /space glyphshow +grestore +/WenQuanYiZenHei 27.000 selectfont +gsave + +86.400000 205.200000 translate +0.000000 rotate +139.350586 0 m /uni591A glyphshow +166.350586 0 m /uni4E2A glyphshow +193.350586 0 m /uni6C49 glyphshow +220.350586 0 m /uni5B57 glyphshow +grestore +/DejaVuSans 27.000 selectfont +gsave + +86.400000 205.200000 translate +0.000000 rotate +247.350586 0 m /space glyphshow +255.933105 0 m /i glyphshow +263.434570 0 m /n glyphshow +280.546875 0 m /space glyphshow +289.129395 0 m /b glyphshow +306.268066 0 m /e glyphshow +322.879395 0 m /t glyphshow +333.465820 0 m /w glyphshow +355.548340 0 m /e glyphshow +372.159668 0 m /e glyphshow +388.770996 0 m /n glyphshow +405.883301 0 m /exclam glyphshow +grestore + +end +showpage diff --git a/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps new file mode 100644 index 000000000000..c97e0bb382c1 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps @@ -0,0 +1,363 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Title: multi_font_type42.eps +%%Creator: Matplotlib v3.4.2.post1635+gbd11b22ee3.d20210813, https://matplotlib.org/ +%%CreationDate: Fri Aug 13 16:50:31 2021 +%%Orientation: portrait +%%BoundingBox: 18 180 594 612 +%%HiResBoundingBox: 18.000000 180.000000 594.000000 612.000000 +%%EndComments +%%BeginProlog +/mpldict 12 dict def +mpldict begin +/_d { bind def } bind def +/m { moveto } _d +/l { lineto } _d +/r { rlineto } _d +/c { curveto } _d +/cl { closepath } _d +/ce { closepath eofill } _d +/box { + m + 1 index 0 r + 0 exch r + neg 0 r + cl + } _d +/clipbox { + box + clip + newpath + } _d +/sc { setcachedevice } _d +%!PS-TrueTypeFont-1.0-2.22937 +%%Title: unknown +%%Creator: Converted from TrueType to type 42 by PPR +15 dict begin +/FontName /DejaVuSans def +/PaintType 0 def +/FontMatrix[1 0 0 1 0 0]def +/FontBBox[-1021 -463 1793 1232]def +/FontType 42 def +/Encoding StandardEncoding def +/FontInfo 10 dict dup begin +/FamilyName (unknown) def +/FullName (unknown) def +/Weight (unknown) def +/Version (unknown) def +/ItalicAngle 0.0 def +/isFixedPitch false def +/UnderlinePosition -130 def +/UnderlineThickness 90 def +end readonly def +/sfnts[<0001000000090080000300106376742000691D390000009C000001FE6670676D +7134766A0000029C000000AB676C7966118399D500000348000007B668656164085DC286 +00000B0000000036686865610D9F077C00000B3800000024686D74783A5706B700000B5C +0000003C6C6F636108C30B6500000B98000000206D617870047C067100000BB800000020 +707265703B07F10000000BD800000568013500B800CB00CB00C100AA009C01A600B80066 +0000007100CB00A002B20085007500B800C301CB0189022D00CB00A600F000D300AA0087 +00CB03AA0400014A003300CB000000D9050200F4015400B4009C01390114013907060400 +044E04B4045204B804E704CD0037047304CD04600473013303A2055605A60556053903C5 +021200C9001F00B801DF007300BA03E9033303BC0444040E00DF03CD03AA00E503AA0404 +000000CB008F00A4007B00B80014016F007F027B0252008F00C705CD009A009A006F00CB +00CD019E01D300F000BA018300D5009803040248009E01D500C100CB00F600830354027F +00000333026600D300C700A400CD008F009A0073040005D5010A00FE022B00A400B4009C +00000062009C0000001D032D05D505D505D505F0007F007B005400A406B80614072301D3 +00B800CB00A601C301EC069300A000D3035C037103DB0185042304A80448008F01390114 +01390360008F05D5019A0614072306660179046004600460047B009C00000277046001AA +00E904600762007B00C5007F027B000000B4025205CD006600BC00660077061000CD013B +01850389008F007B0000001D00CD074A042F009C009C0000077D006F0000006F0335006A +006F007B00AE00B2002D0396008F027B00F600830354063705F6008F009C04E10266008F +018D02F600CD03440029006604EE00730000140000960000B707060504030201002C2010 +B002254964B040515820C859212D2CB002254964B040515820C859212D2C20100720B000 +50B00D7920B8FFFF5058041B0559B0051CB0032508B0042523E120B00050B00D7920B8FF +FF5058041B0559B0051CB0032508E12D2C4B505820B0FD454459212D2CB002254560442D +2C4B5358B00225B0022545445921212D2C45442D2CB00225B0022549B00525B005254960 +B0206368208A108A233A8A10653A2D00000201350000020005D5000300090035400F0700 +8304810208070501030400000A10FC4BB00B5458B90000FFC038593CEC32393931002FE4 +FCCC3001B6000B200B500B035D253315231133110323030135CBCBCB14A215FEFE05D5FD +71FE9B0165000001FFFA000004E905D50007004A400E0602950081040140031C00400508 +10D4E4FCE431002FF4EC3230014BB00A5458BD00080040000100080008FFC03811373859 +401300091F00100110021F071009400970099F09095D03211521112311210604EFFDEECB +FDEE05D5AAFAD5052B000002007BFFE3042D047B000A002500BC4027191F0B17090E00A9 +1706B90E1120861FBA1CB923B8118C170C001703180D09080B1F030814452610FCECCCD4 +EC323211393931002FC4E4F4FCF4EC10C6EE10EE11391139123930406E301D301E301F30 +20302130223F27401D401E401F402040214022501D501E501F5020502150225027702785 +1D871E871F8720872185229027A027F0271E301E301F30203021401E401F40204021501E +501F50205021601E601F60206021701E701F70207021801E801F80208021185D015D0122 +061514163332363D01371123350E01232226353436332135342623220607353E01333216 +02BEDFAC816F99B9B8B83FBC88ACCBFDFB0102A79760B65465BE5AF3F00233667B6273D9 +B4294CFD81AA6661C1A2BDC0127F8B2E2EAA2727FC00000200BAFFE304A40614000B001C +0038401903B90C0F09B918158C0FB81B971900121247180C06081A461D10FCEC3232F4EC +31002FECE4F4C4EC10C6EE30B6601E801EA01E03015D013426232206151416333236013E +01333200111002232226271523113303E5A79292A7A79292A7FD8E3AB17BCC00FFFFCC7B +B13AB9B9022FCBE7E7CBCBE7E702526461FEBCFEF8FEF8FEBC6164A8061400020071FFE3 +047F047B0014001B00704024001501098608880515A90105B90C01BB18B912B80C8C1C1B +1502081508004B02120F451C10FCECF4ECC4111239310010E4F4ECE410EE10EE10F4EE11 +12393040293F1D701DA01DD01DF01D053F003F013F023F153F1B052C072F082F092C0A6F +006F016F026F156F1B095D71015D0115211E0133323637150E0123200011100033320007 +2E0123220607047FFCB20CCDB76AC76263D06BFEF4FEC70129FCE20107B802A5889AB90E +025E5ABEC73434AE2A2C0138010A01130143FEDDC497B4AE9E00000100BA000004640614 +001300344019030900030E0106870E11B80C970A010208004E0D09080B461410FCEC32F4 +EC31002F3CECF4C4EC1112173930B2601501015D0111231134262322061511231133113E +013332160464B87C7C95ACB9B942B375C1C602A4FD5C029E9F9EBEA4FD870614FD9E6564 +EF00000200C100000179061400030007002B400E06BE04B100BC020501080400460810FC +3CEC3231002FE4FCEC30400B1009400950096009700905015D1333112311331523C1B8B8 +B8B80460FBA00614E900000100BA00000464047B001300364019030900030E0106870E11 +B80CBC0A010208004E0D09080B461410FCEC32F4EC31002F3CE4F4C4EC1112173930B460 +15CF1502015D0111231134262322061511231133153E013332160464B87C7C95ACB9B942 +B375C1C602A4FD5C029E9F9EBEA4FD870460AE6564EF000100BA0000034A047B00110030 +4014060B0700110B03870EB809BC070A06080008461210FCC4EC3231002FE4F4ECC4D4CC +11123930B450139F1302015D012E012322061511231133153E0133321617034A1F492C9C +A7B9B93ABA85132E1C03B41211CBBEFDB20460AE6663050500010037000002F2059E0013 +003840190E05080F03A9001101BC08870A0B08090204000810120E461410FC3CC4FC3CC4 +32393931002FECF43CC4EC3211393930B2AF1501015D01112115211114163B0115232226 +3511233533110177017BFE854B73BDBDD5A28787059EFEC28FFDA0894E9A9FD202608F01 +3E0000010056000006350460000C01EB404905550605090A0904550A0903550A0B0A0255 +01020B0B0A061107080705110405080807021103020C000C011100000C420A0502030603 +00BF0B080C0B0A09080605040302010B07000D10D44BB00A544BB011545B4BB012545B4B +B013545B4BB00B545B58B9000000403859014BB00C544BB00D545B4BB010545B58B90000 +FFC03859CC173931002F3CEC32321739304B5358071005ED071008ED071008ED071005ED +071008ED071005ED0705ED071008ED59220140FF050216021605220A350A49024905460A +400A5B025B05550A500A6E026E05660A79027F0279057F05870299029805940ABC02BC05 +CE02C703CF051D0502090306040B050A080B09040B050C1502190316041A051B081B0914 +0B150C2500250123022703210425052206220725082709240A210B230C39033604360839 +0C300E460248034604400442054006400740084409440A440B400E400E56005601560250 +0451055206520750085309540A550B6300640165026A0365046A056A066A076E09610B67 +0C6F0E7500750179027D0378047D057A067F067A077F07780879097F097B0A760B7D0C87 +0288058F0E97009701940293039C049B05980698079908402F960C9F0EA600A601A402A4 +03AB04AB05A906A907AB08A40CAF0EB502B103BD04BB05B809BF0EC402C303CC04CA0579 +5D005D13331B01331B013301230B012356B8E6E5D9E6E5B8FEDBD9F1F2D90460FC96036A +FC96036AFBA00396FC6A00000001000000025999D203C60C5F0F3CF5001F080000000000 +D17E0EE400000000D17E0EE4F7D6FC4C0E5909DC00000008000000000000000000010000 +076DFE1D00000EFEF7D6FA510E5900010000000000000000000000000000000F04CD0066 +0000000002AA0000028B00000335013504E3FFFA04E7007B051400BA04EC0071051200BA +023900C1051200BA034A00BA03230037068B0056000000000000000000000031006900FF +014B01B501F102190255028C02C903DB00010000000F0354002B0068000C000200100099 +000800000415021600080004B8028040FFFBFE03FA1403F92503F83203F79603F60E03F5 +FE03F4FE03F32503F20E03F19603F02503EF8A4105EFFE03EE9603ED9603ECFA03EBFA03 +EAFE03E93A03E84203E7FE03E63203E5E45305E59603E48A4105E45303E3E22F05E3FA03 +E22F03E1FE03E0FE03DF3203DE1403DD9603DCFE03DB1203DA7D03D9BB03D8FE03D68A41 +05D67D03D5D44705D57D03D44703D3D21B05D3FE03D21B03D1FE03D0FE03CFFE03CEFE03 +CD9603CCCB1E05CCFE03CB1E03CA3203C9FE03C6851105C61C03C51603C4FE03C3FE03C2 +FE03C1FE03C0FE03BFFE03BEFE03BDFE03BCFE03BBFE03BA1103B9862505B9FE03B8B7BB +05B8FE03B7B65D05B7BB03B78004B6B52505B65D40FF03B64004B52503B4FE03B39603B2 +FE03B1FE03B0FE03AFFE03AE6403AD0E03ACAB2505AC6403ABAA1205AB2503AA1203A98A +4105A9FA03A8FE03A7FE03A6FE03A51203A4FE03A3A20E05A33203A20E03A16403A08A41 +05A096039FFE039E9D0C059EFE039D0C039C9B19059C64039B9A10059B19039A1003990A +0398FE0397960D0597FE03960D03958A410595960394930E05942803930E0392FA039190 +BB0591FE03908F5D0590BB039080048F8E25058F5D038F40048E25038DFE038C8B2E058C +FE038B2E038A8625058A410389880B05891403880B038786250587640386851105862503 +85110384FE038382110583FE0382110381FE0380FE037FFE0340FF7E7D7D057EFE037D7D +037C64037B5415057B25037AFE0379FE03780E03770C03760A0375FE0374FA0373FA0372 +FA0371FA0370FE036FFE036EFE036C21036BFE036A1142056A530369FE03687D03671142 +0566FE0365FE0364FE0363FE0362FE03613A0360FA035E0C035DFE035BFE035AFE035958 +0A0559FA03580A035716190557320356FE03555415055542035415035301100553180352 +1403514A130551FE03500B034FFE034E4D10054EFE034D10034CFE034B4A13054BFE034A +4910054A1303491D0D05491003480D0347FE0346960345960344FE0343022D0543FA0342 +BB03414B0340FE033FFE033E3D12053E14033D3C0F053D12033C3B0D053C40FF0F033B0D +033AFE0339FE033837140538FA033736100537140336350B05361003350B03341E03330D +0332310B0532FE03310B03302F0B05300D032F0B032E2D09052E10032D09032C32032B2A +25052B64032A2912052A25032912032827250528410327250326250B05260F03250B0324 +FE0323FE03220F03210110052112032064031FFA031E1D0D051E64031D0D031C1142051C +FE031BFA031A42031911420519FE031864031716190517FE031601100516190315FE0314 +FE0313FE031211420512FE0311022D05114203107D030F64030EFE030D0C16050DFE030C +0110050C16030BFE030A100309FE0308022D0508FE030714030664030401100504FE0340 +1503022D0503FE0302011005022D0301100300FE0301B80164858D012B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B002B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B1D00>]def +/CharStrings 13 dict dup begin +/.notdef 0 def +/space 3 def +/exclam 4 def +/b 7 def +/a 6 def +/e 8 def +/h 9 def +/i 10 def +/n 11 def +/r 12 def +/t 13 def +/T 5 def +/w 14 def +end readonly def + +systemdict/resourcestatus known + {42 /FontType resourcestatus + {pop pop false}{true}ifelse} + {true}ifelse +{/TrueDict where{pop}{(%%[ Error: no TrueType rasterizer ]%%)= flush}ifelse +/FontType 3 def + /TrueState 271 string def + TrueDict begin sfnts save + 72 0 matrix defaultmatrix dtransform dup + mul exch dup mul add sqrt cvi 0 72 matrix + defaultmatrix dtransform dup mul exch dup + mul add sqrt cvi 3 -1 roll restore + TrueState initer end + /BuildGlyph{exch begin + CharStrings dup 2 index known + {exch}{exch pop /.notdef}ifelse + get dup xcheck + {currentdict systemdict begin begin exec end end} + {TrueDict begin /bander load cvlit exch TrueState render end} + ifelse + end}bind def + /BuildChar{ + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec + }bind def +}if + +FontName currentdict end definefont pop +%!PS-TrueTypeFont-1.0-0.58982 +%%Title: unknown +%%Creator: Converted from TrueType to type 42 by PPR +15 dict begin +/FontName /WenQuanYiZenHei def +/PaintType 0 def +/FontMatrix[1 0 0 1 0 0]def +/FontBBox[-126 -297 1051 963]def +/FontType 42 def +/Encoding StandardEncoding def +/FontInfo 10 dict dup begin +/FamilyName (unknown) def +/FullName (unknown) def +/Weight (unknown) def +/Version (unknown) def +/ItalicAngle 0.0 def +/isFixedPitch false def +/UnderlinePosition -230 def +/UnderlineThickness 51 def +end readonly def +/sfnts[<00010000000700400002003063767420002202880000007C00000004676C7966 +422AC14C000000800000028E68656164F2831BDF00000310000000366868656107EC01A3 +0000034800000024686D747805C1004E0000036C0000001A6C6F636101E4013300000388 +000000126D617870008A02690000039C00000020002202880002000DFF8503D50341000D +0024000005260736351134271637061511140106072E0127020726273E01371617371716 +17071617160223292A04042A290301B4250C80D74AC4F7122795F54C171806050B0B0958 +74627B04044D4C014C4D4D04044D4DFEB44C01D91A2B27C278FEE18B2C194DEFA3100C03 +0806050E9B5946000002003BFF8703A00343001B00360000250727060726273637363716 +170721060706042726271624372306071307270607262736373637161706072106040726 +273624372306070245377F7879061FD07349411D232801427CDC6BFEFE860514D9018D76 +F82728223A6D61670A22B759352C22271516015171FE8FDF0F1DBA014069F8171759377F +4B222C2036614049201926D97A3C370D2B2628ABAE201C013335794E272B1D40653F461B +131C1ABAF71E28210AB3921916000002001FFF7C03D60369002A00370000010617262321 +151407062736271E01363D0121220736271633213537212207362716332117060F011521 +320123350527371707211523352103D603033A3BFECA1E2B720C24215A10FEB13A3A0303 +3A3A014FA5FEB53A3B04043B3A01BC081F189F01363BFCE74801994E2D6B20019F49FD2F +011D1F2003FE261C2501322604010C1BEA03201F03499703201F0341081592290118BF01 +37414A2EBE8500050010FF8803DF03350016001B00230027002D00002526032207362716 +33210207161706072627060726273613161736370126273E011317031307273F01262737 +16170264721E201F03033C3D01591E916EB82915A46F8AD01B25E0441A5E7815FD7B2A3E +2C5E5929741F40953FA845523C564AD3CF011902212103FEADDBA66510265EA8AE512318 +4D02A4F9B4BBF2FCCE180251D0010115FE850193318832204C4234465000000000010000 +0000E666CBB292A95F0F3CF5003F040000000000C7BE78E900000000C7BE78E9FF7FFED0 +043403DA0000000800020000000000000001000003DAFED0005C0455FF7FFE7804340001 +00000000000000000000000000000005017600220000000000000000000000000400000D +003B001F00100000000000000000000000000040009D00F3014700000001000000080165 +002800D10012000200000001000100000040002E0006000200>]def +/CharStrings 5 dict dup begin +/.notdef 0 def +/uni6C49 7 def +/uni4E2A 4 def +/uni591A 5 def +/uni5B57 6 def +end readonly def + +systemdict/resourcestatus known + {42 /FontType resourcestatus + {pop pop false}{true}ifelse} + {true}ifelse +{/TrueDict where{pop}{(%%[ Error: no TrueType rasterizer ]%%)= flush}ifelse +/FontType 3 def + /TrueState 271 string def + TrueDict begin sfnts save + 72 0 matrix defaultmatrix dtransform dup + mul exch dup mul add sqrt cvi 0 72 matrix + defaultmatrix dtransform dup mul exch dup + mul add sqrt cvi 3 -1 roll restore + TrueState initer end + /BuildGlyph{exch begin + CharStrings dup 2 index known + {exch}{exch pop /.notdef}ifelse + get dup xcheck + {currentdict systemdict begin begin exec end end} + {TrueDict begin /bander load cvlit exch TrueState render end} + ifelse + end}bind def + /BuildChar{ + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec + }bind def +}if + +FontName currentdict end definefont pop +end +%%EndProlog +mpldict begin +18 180 translate +576 432 0 0 clipbox +gsave +0 0 m +576 0 l +576 432 l +0 432 l +cl +1.000 setgray +fill +grestore +0.000 setgray +/DejaVuSans 27.000 selectfont +gsave + +86.400000 205.200000 translate +0.000000 rotate +0.000000 0 m /T glyphshow +16.492676 0 m /h glyphshow +33.604980 0 m /e glyphshow +50.216309 0 m /r glyphshow +61.316895 0 m /e glyphshow +77.928223 0 m /space glyphshow +86.510742 0 m /a glyphshow +103.056152 0 m /r glyphshow +114.156738 0 m /e glyphshow +130.768066 0 m /space glyphshow +grestore +/WenQuanYiZenHei 27.000 selectfont +gsave + +86.400000 205.200000 translate +0.000000 rotate +139.350586 0 m /uni591A glyphshow +166.350586 0 m /uni4E2A glyphshow +193.350586 0 m /uni6C49 glyphshow +220.350586 0 m /uni5B57 glyphshow +grestore +/DejaVuSans 27.000 selectfont +gsave + +86.400000 205.200000 translate +0.000000 rotate +247.350586 0 m /space glyphshow +255.933105 0 m /i glyphshow +263.434570 0 m /n glyphshow +280.546875 0 m /space glyphshow +289.129395 0 m /b glyphshow +306.268066 0 m /e glyphshow +322.879395 0 m /t glyphshow +333.465820 0 m /w glyphshow +355.548340 0 m /e glyphshow +372.159668 0 m /e glyphshow +388.770996 0 m /n glyphshow +405.883301 0 m /exclam glyphshow +grestore + +end +showpage From 913ad4c2b74b0492e76f2d2eb1f3259954ae0d35 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Fri, 13 Aug 2021 17:55:17 +0530 Subject: [PATCH 28/32] Specify font number for TTC fonts --- lib/matplotlib/backends/_backend_pdf_ps.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/_backend_pdf_ps.py b/lib/matplotlib/backends/_backend_pdf_ps.py index ef1568f142b0..d51a50581378 100644 --- a/lib/matplotlib/backends/_backend_pdf_ps.py +++ b/lib/matplotlib/backends/_backend_pdf_ps.py @@ -2,6 +2,7 @@ Common functionality between the PDF and PS backends. """ +import os from io import BytesIO import functools @@ -37,7 +38,11 @@ def get_glyphs_subset(fontfile, characters): options = subset.Options(glyph_names=True, recommended_glyphs=True) # prevent subsetting FontForge Timestamp and other tables - options.drop_tables += ['FFTM', 'PfEd'] + options.drop_tables += ['FFTM', 'PfEd', 'BDF'] + + # if fontfile is a ttc, specify font number + if os.path.splitext(fontfile)[1] == ".ttc": + options.font_number = 0 with subset.load_font(fontfile, options) as font: subsetter = subset.Subsetter(options=options) From 6fe8f44dfbf304c03cb594a50a6e0d3b39b1bf52 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Fri, 13 Aug 2021 18:09:38 +0530 Subject: [PATCH 29/32] Flake8 fixes --- lib/matplotlib/backends/backend_pdf.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index fac289fce37e..9d33a7bda630 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -2380,7 +2380,8 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # the regular text show command (TJ) with appropriate kerning between # chunks, whereas multibyte characters use the XObject command (Do). else: - # List of (ft_object, start_x, [prev_kern, char, char, ...]), w/o zero kerns. + # List of (ft_object, start_x, [prev_kern, char, char, ...]), + # w/o zero kerns. singlebyte_chunks = [] # List of (ft_object, start_x, glyph_index). multibyte_glyphs = [] @@ -2397,7 +2398,9 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): singlebyte_chunks[-1][2].append(item.char) prev_was_multibyte = False else: - multibyte_glyphs.append((item.ft_object, item.x, item.glyph_idx)) + multibyte_glyphs.append( + (item.ft_object, item.x, item.glyph_idx) + ) prev_was_multibyte = True # Do the rotation and global translation as a single matrix # concatenation up front @@ -2425,7 +2428,9 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): self.file.output(Op.end_text) # Then emit all the multibyte characters, one at a time. for ft_object, start_x, glyph_idx in multibyte_glyphs: - self._draw_xobject_glyph(ft_object, fontsize, glyph_idx, start_x, 0) + self._draw_xobject_glyph( + ft_object, fontsize, glyph_idx, start_x, 0 + ) self.file.output(Op.grestore) def _draw_xobject_glyph(self, ft_object, fontsize, glyph_idx, x, y): From a3e7f03c52cd931954966266bb08cd74596c88ff Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Wed, 18 Aug 2021 05:37:51 +0530 Subject: [PATCH 30/32] Fix memory leak and render tofu --- src/ft2font.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 0da6a52df9bf..088e23d1d703 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -377,6 +377,10 @@ FT2Font::~FT2Font() if (face) { FT_Done_Face(face); } + + for (size_t i = 0; i < fallbacks.size(); i++) { + Py_DECREF(fallbacks[i]->get_pyfont()); + } } void FT2Font::clear() @@ -508,7 +512,7 @@ void FT2Font::set_text( FT_BBox glyph_bbox; FT_Pos last_advance; - FT_UInt final_glyph_index; + FT_UInt final_glyph_index = 0; FT_Error charcode_error, glyph_error; FT2Font *ft_object_with_glyph = this; bool was_found = load_char_with_fallback(ft_object_with_glyph, final_glyph_index, glyphs, @@ -516,7 +520,12 @@ void FT2Font::set_text( charcode_error, glyph_error, false); if (!was_found) { ft_glyph_warn((FT_ULong)codepoints[n]); - continue; + + // render tofu + // ft_object_with_glyph == this + char_to_font[codepoints[n]] = ft_object_with_glyph; + glyph_to_font[final_glyph_index] = ft_object_with_glyph; + ft_object_with_glyph->load_glyph(final_glyph_index, flags, ft_object_with_glyph, false); } glyph_index = final_glyph_index; From 986ecfe21f7b362316c874ebba5776a6b8199a6f Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Wed, 18 Aug 2021 05:38:08 +0530 Subject: [PATCH 31/32] Check if fallback font is available --- lib/matplotlib/tests/test_backend_ps.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_backend_ps.py b/lib/matplotlib/tests/test_backend_ps.py index 50cdf03a352e..80ee74e10d61 100644 --- a/lib/matplotlib/tests/test_backend_ps.py +++ b/lib/matplotlib/tests/test_backend_ps.py @@ -7,7 +7,7 @@ import matplotlib as mpl import matplotlib.pyplot as plt -from matplotlib import cbook, patheffects +from matplotlib import cbook, patheffects, font_manager as fm from matplotlib.testing.decorators import check_figures_equal, image_comparison from matplotlib.cbook import MatplotlibDeprecationWarning @@ -238,6 +238,10 @@ def test_linedash(): @image_comparison(["multi_font_type3.eps"]) def test_multi_font_type3(): + fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) + if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": + pytest.skip("Font may be missing") + plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) plt.rc('ps', fonttype=3) @@ -247,6 +251,10 @@ def test_multi_font_type3(): @image_comparison(["multi_font_type42.eps"]) def test_multi_font_type42(): + fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) + if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": + pytest.skip("Font may be missing") + plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) plt.rc('ps', fonttype=42) From 1c85f46bff1e07250dc635c2497f2dcdc8316f7d Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Wed, 18 Aug 2021 06:01:49 +0530 Subject: [PATCH 32/32] Check for Agg as well --- lib/matplotlib/tests/test_agg.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_agg.py b/lib/matplotlib/tests/test_agg.py index bfbda95f52a3..df67c397dc10 100644 --- a/lib/matplotlib/tests/test_agg.py +++ b/lib/matplotlib/tests/test_agg.py @@ -1,4 +1,5 @@ import io +from pathlib import Path import numpy as np from numpy.testing import assert_array_almost_equal @@ -7,7 +8,9 @@ from matplotlib import ( - collections, path, pyplot as plt, transforms as mtransforms, rcParams) + collections, path, pyplot as plt, transforms as mtransforms, rcParams, + font_manager as fm +) from matplotlib.image import imread from matplotlib.figure import Figure from matplotlib.testing.decorators import image_comparison @@ -255,6 +258,10 @@ def test_draw_path_collection_error_handling(): @image_comparison(["font_fallback.png"]) def test_font_fallback(): + fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) + if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": + pytest.skip("Font may be missing") + plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=15) fig, ax = plt.subplots()