Skip to content

Commit 3eda5a3

Browse files
authored
Merge pull request #27891 from QuLogic/ft2font-cleanup
Refactor some parts of ft2font extension
2 parents aa70f69 + 276fade commit 3eda5a3

File tree

6 files changed

+149
-261
lines changed

6 files changed

+149
-261
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ft2font classes are now final
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
The ft2font classes `.ft2font.FT2Font`, and `.ft2font.FT2Image` are now final
5+
and can no longer be subclassed.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
``ft2font.FT2Image.draw_rect`` and ``ft2font.FT2Font.get_xys``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
... have been removed as they are unused.

lib/matplotlib/ft2font.pyi

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import BinaryIO, Literal, TypedDict, overload
1+
from typing import BinaryIO, Literal, TypedDict, final, overload
22

33
import numpy as np
44
from numpy.typing import NDArray
@@ -158,6 +158,7 @@ class _SfntPcltDict(TypedDict):
158158
widthType: int
159159
serifStyle: int
160160

161+
@final
161162
class FT2Font:
162163
ascender: int
163164
bbox: tuple[int, int, int, int]
@@ -223,7 +224,6 @@ class FT2Font:
223224
@overload
224225
def get_sfnt_table(self, name: Literal["pclt"]) -> _SfntPcltDict | None: ...
225226
def get_width_height(self) -> tuple[int, int]: ...
226-
def get_xys(self, antialiased: bool = ...) -> NDArray[np.float64]: ...
227227
def load_char(self, charcode: int, flags: int = ...) -> Glyph: ...
228228
def load_glyph(self, glyphindex: int, flags: int = ...) -> Glyph: ...
229229
def select_charmap(self, i: int) -> None: ...
@@ -233,11 +233,12 @@ class FT2Font:
233233
self, string: str, angle: float = ..., flags: int = ...
234234
) -> NDArray[np.float64]: ...
235235

236+
@final
236237
class FT2Image: # TODO: When updating mypy>=1.4, subclass from Buffer.
237238
def __init__(self, width: float, height: float) -> None: ...
238-
def draw_rect(self, x0: float, y0: float, x1: float, y1: float) -> None: ...
239239
def draw_rect_filled(self, x0: float, y0: float, x1: float, y1: float) -> None: ...
240240

241+
@final
241242
class Glyph:
242243
width: int
243244
height: int

src/ft2font.cpp

Lines changed: 69 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
/* -*- mode: c++; c-basic-offset: 4 -*- */
22

3-
#define NO_IMPORT_ARRAY
4-
53
#include <algorithm>
4+
#include <charconv>
65
#include <iterator>
76
#include <set>
87
#include <sstream>
98
#include <stdexcept>
109
#include <string>
10+
#include <vector>
1111

1212
#include "ft2font.h"
1313
#include "mplutils.h"
14-
#include "numpy_cpp.h"
15-
#include "py_exceptions.h"
1614

1715
#ifndef M_PI
1816
#define M_PI 3.14159265358979323846264338328
@@ -65,12 +63,12 @@ void throw_ft_error(std::string message, FT_Error error) {
6563
throw std::runtime_error(os.str());
6664
}
6765

68-
FT2Image::FT2Image() : m_dirty(true), m_buffer(NULL), m_width(0), m_height(0)
66+
FT2Image::FT2Image() : m_buffer(NULL), m_width(0), m_height(0)
6967
{
7068
}
7169

7270
FT2Image::FT2Image(unsigned long width, unsigned long height)
73-
: m_dirty(true), m_buffer(NULL), m_width(0), m_height(0)
71+
: m_buffer(NULL), m_width(0), m_height(0)
7472
{
7573
resize(width, height);
7674
}
@@ -104,8 +102,6 @@ void FT2Image::resize(long width, long height)
104102
if (numBytes && m_buffer) {
105103
memset(m_buffer, 0, numBytes);
106104
}
107-
108-
m_dirty = true;
109105
}
110106

111107
void FT2Image::draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y)
@@ -143,29 +139,6 @@ void FT2Image::draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y)
143139
} else {
144140
throw std::runtime_error("Unknown pixel mode");
145141
}
146-
147-
m_dirty = true;
148-
}
149-
150-
void FT2Image::draw_rect(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1)
151-
{
152-
if (x0 > m_width || x1 > m_width || y0 > m_height || y1 > m_height) {
153-
throw std::runtime_error("Rect coords outside image bounds");
154-
}
155-
156-
size_t top = y0 * m_width;
157-
size_t bottom = y1 * m_width;
158-
for (size_t i = x0; i < x1 + 1; ++i) {
159-
m_buffer[i + top] = 255;
160-
m_buffer[i + bottom] = 255;
161-
}
162-
163-
for (size_t j = y0 + 1; j < y1; ++j) {
164-
m_buffer[x0 + j * m_width] = 255;
165-
m_buffer[x1 + j * m_width] = 255;
166-
}
167-
168-
m_dirty = true;
169142
}
170143

171144
void
@@ -181,92 +154,51 @@ FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1,
181154
m_buffer[i + j * m_width] = 255;
182155
}
183156
}
184-
185-
m_dirty = true;
186-
}
187-
188-
static void ft_glyph_warn(FT_ULong charcode, std::set<FT_String*> family_names)
189-
{
190-
PyObject *text_helpers = NULL, *tmp = NULL;
191-
std::set<FT_String*>::iterator it = family_names.begin();
192-
std::stringstream ss;
193-
ss<<*it;
194-
while(++it != family_names.end()){
195-
ss<<", "<<*it;
196-
}
197-
198-
if (!(text_helpers = PyImport_ImportModule("matplotlib._text_helpers")) ||
199-
!(tmp = PyObject_CallMethod(text_helpers,
200-
"warn_on_missing_glyph", "(k, s)",
201-
charcode, ss.str().c_str()))) {
202-
goto exit;
203-
}
204-
exit:
205-
Py_XDECREF(text_helpers);
206-
Py_XDECREF(tmp);
207-
if (PyErr_Occurred()) {
208-
throw mpl::exception();
209-
}
210157
}
211158

212-
// ft_outline_decomposer should be passed to FT_Outline_Decompose. On the
213-
// first pass, vertices and codes are set to NULL, and index is simply
214-
// incremented for each vertex that should be inserted, so that it is set, at
215-
// the end, to the total number of vertices. On a second pass, vertices and
216-
// codes should point to correctly sized arrays, and index set again to zero,
217-
// to get fill vertices and codes with the outline decomposition.
159+
// ft_outline_decomposer should be passed to FT_Outline_Decompose.
218160
struct ft_outline_decomposer
219161
{
220-
int index;
221-
double* vertices;
222-
unsigned char* codes;
162+
std::vector<double> &vertices;
163+
std::vector<unsigned char> &codes;
223164
};
224165

225166
static int
226167
ft_outline_move_to(FT_Vector const* to, void* user)
227168
{
228169
ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user);
229-
if (d->codes) {
230-
if (d->index) {
231-
// Appending CLOSEPOLY is important to make patheffects work.
232-
*(d->vertices++) = 0;
233-
*(d->vertices++) = 0;
234-
*(d->codes++) = CLOSEPOLY;
235-
}
236-
*(d->vertices++) = to->x * (1. / 64.);
237-
*(d->vertices++) = to->y * (1. / 64.);
238-
*(d->codes++) = MOVETO;
239-
}
240-
d->index += d->index ? 2 : 1;
170+
if (!d->vertices.empty()) {
171+
// Appending CLOSEPOLY is important to make patheffects work.
172+
d->vertices.push_back(0);
173+
d->vertices.push_back(0);
174+
d->codes.push_back(CLOSEPOLY);
175+
}
176+
d->vertices.push_back(to->x * (1. / 64.));
177+
d->vertices.push_back(to->y * (1. / 64.));
178+
d->codes.push_back(MOVETO);
241179
return 0;
242180
}
243181

244182
static int
245183
ft_outline_line_to(FT_Vector const* to, void* user)
246184
{
247185
ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user);
248-
if (d->codes) {
249-
*(d->vertices++) = to->x * (1. / 64.);
250-
*(d->vertices++) = to->y * (1. / 64.);
251-
*(d->codes++) = LINETO;
252-
}
253-
d->index++;
186+
d->vertices.push_back(to->x * (1. / 64.));
187+
d->vertices.push_back(to->y * (1. / 64.));
188+
d->codes.push_back(LINETO);
254189
return 0;
255190
}
256191

257192
static int
258193
ft_outline_conic_to(FT_Vector const* control, FT_Vector const* to, void* user)
259194
{
260195
ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user);
261-
if (d->codes) {
262-
*(d->vertices++) = control->x * (1. / 64.);
263-
*(d->vertices++) = control->y * (1. / 64.);
264-
*(d->vertices++) = to->x * (1. / 64.);
265-
*(d->vertices++) = to->y * (1. / 64.);
266-
*(d->codes++) = CURVE3;
267-
*(d->codes++) = CURVE3;
268-
}
269-
d->index += 2;
196+
d->vertices.push_back(control->x * (1. / 64.));
197+
d->vertices.push_back(control->y * (1. / 64.));
198+
d->vertices.push_back(to->x * (1. / 64.));
199+
d->vertices.push_back(to->y * (1. / 64.));
200+
d->codes.push_back(CURVE3);
201+
d->codes.push_back(CURVE3);
270202
return 0;
271203
}
272204

@@ -275,18 +207,15 @@ ft_outline_cubic_to(
275207
FT_Vector const* c1, FT_Vector const* c2, FT_Vector const* to, void* user)
276208
{
277209
ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user);
278-
if (d->codes) {
279-
*(d->vertices++) = c1->x * (1. / 64.);
280-
*(d->vertices++) = c1->y * (1. / 64.);
281-
*(d->vertices++) = c2->x * (1. / 64.);
282-
*(d->vertices++) = c2->y * (1. / 64.);
283-
*(d->vertices++) = to->x * (1. / 64.);
284-
*(d->vertices++) = to->y * (1. / 64.);
285-
*(d->codes++) = CURVE4;
286-
*(d->codes++) = CURVE4;
287-
*(d->codes++) = CURVE4;
288-
}
289-
d->index += 3;
210+
d->vertices.push_back(c1->x * (1. / 64.));
211+
d->vertices.push_back(c1->y * (1. / 64.));
212+
d->vertices.push_back(c2->x * (1. / 64.));
213+
d->vertices.push_back(c2->y * (1. / 64.));
214+
d->vertices.push_back(to->x * (1. / 64.));
215+
d->vertices.push_back(to->y * (1. / 64.));
216+
d->codes.push_back(CURVE4);
217+
d->codes.push_back(CURVE4);
218+
d->codes.push_back(CURVE4);
290219
return 0;
291220
}
292221

@@ -296,52 +225,41 @@ static FT_Outline_Funcs ft_outline_funcs = {
296225
ft_outline_conic_to,
297226
ft_outline_cubic_to};
298227

299-
PyObject*
300-
FT2Font::get_path()
228+
void
229+
FT2Font::get_path(std::vector<double> &vertices, std::vector<unsigned char> &codes)
301230
{
302231
if (!face->glyph) {
303-
PyErr_SetString(PyExc_RuntimeError, "No glyph loaded");
304-
return NULL;
305-
}
306-
ft_outline_decomposer decomposer = {};
307-
if (FT_Error error =
308-
FT_Outline_Decompose(
309-
&face->glyph->outline, &ft_outline_funcs, &decomposer)) {
310-
PyErr_Format(PyExc_RuntimeError,
311-
"FT_Outline_Decompose failed with error 0x%x", error);
312-
return NULL;
313-
}
314-
if (!decomposer.index) { // Don't append CLOSEPOLY to null glyphs.
315-
npy_intp vertices_dims[2] = { 0, 2 };
316-
numpy::array_view<double, 2> vertices(vertices_dims);
317-
npy_intp codes_dims[1] = { 0 };
318-
numpy::array_view<unsigned char, 1> codes(codes_dims);
319-
return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj());
320-
}
321-
npy_intp vertices_dims[2] = { decomposer.index + 1, 2 };
322-
numpy::array_view<double, 2> vertices(vertices_dims);
323-
npy_intp codes_dims[1] = { decomposer.index + 1 };
324-
numpy::array_view<unsigned char, 1> codes(codes_dims);
325-
decomposer.index = 0;
326-
decomposer.vertices = vertices.data();
327-
decomposer.codes = codes.data();
328-
if (FT_Error error =
329-
FT_Outline_Decompose(
330-
&face->glyph->outline, &ft_outline_funcs, &decomposer)) {
331-
PyErr_Format(PyExc_RuntimeError,
332-
"FT_Outline_Decompose failed with error 0x%x", error);
333-
return NULL;
334-
}
335-
*(decomposer.vertices++) = 0;
336-
*(decomposer.vertices++) = 0;
337-
*(decomposer.codes++) = CLOSEPOLY;
338-
return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj());
232+
throw std::runtime_error("No glyph loaded");
233+
}
234+
ft_outline_decomposer decomposer = {
235+
vertices,
236+
codes,
237+
};
238+
// We can make a close-enough estimate based on number of points and number of
239+
// contours (which produce a MOVETO each), though it's slightly underestimating due
240+
// to higher-order curves.
241+
size_t estimated_points = static_cast<size_t>(face->glyph->outline.n_contours) +
242+
static_cast<size_t>(face->glyph->outline.n_points);
243+
vertices.reserve(2 * estimated_points);
244+
codes.reserve(estimated_points);
245+
if (FT_Error error = FT_Outline_Decompose(
246+
&face->glyph->outline, &ft_outline_funcs, &decomposer)) {
247+
throw std::runtime_error("FT_Outline_Decompose failed with error " +
248+
std::to_string(error));
249+
}
250+
if (vertices.empty()) { // Don't append CLOSEPOLY to null glyphs.
251+
return;
252+
}
253+
vertices.push_back(0);
254+
vertices.push_back(0);
255+
codes.push_back(CLOSEPOLY);
339256
}
340257

341258
FT2Font::FT2Font(FT_Open_Args &open_args,
342259
long hinting_factor_,
343-
std::vector<FT2Font *> &fallback_list)
344-
: image(), face(NULL)
260+
std::vector<FT2Font *> &fallback_list,
261+
FT2Font::WarnFunc warn)
262+
: ft_glyph_warn(warn), image(), face(NULL)
345263
{
346264
clear();
347265

@@ -771,29 +689,6 @@ void FT2Font::draw_glyphs_to_bitmap(bool antialiased)
771689
}
772690
}
773691

774-
void FT2Font::get_xys(bool antialiased, std::vector<double> &xys)
775-
{
776-
for (size_t n = 0; n < glyphs.size(); n++) {
777-
778-
FT_Error error = FT_Glyph_To_Bitmap(
779-
&glyphs[n], antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1);
780-
if (error) {
781-
throw_ft_error("Could not convert glyph to bitmap", error);
782-
}
783-
784-
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n];
785-
786-
// bitmap left and top in pixel, string bbox in subpixel
787-
FT_Int x = (FT_Int)(bitmap->left - bbox.xMin * (1. / 64.));
788-
FT_Int y = (FT_Int)(bbox.yMax * (1. / 64.) - bitmap->top + 1);
789-
// make sure the index is non-neg
790-
x = x < 0 ? 0 : x;
791-
y = y < 0 ? 0 : y;
792-
xys.push_back(x);
793-
xys.push_back(y);
794-
}
795-
}
796-
797692
void FT2Font::draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, bool antialiased)
798693
{
799694
FT_Vector sub_offset;
@@ -819,7 +714,8 @@ void FT2Font::draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd,
819714
im.draw_bitmap(&bitmap->bitmap, x + bitmap->left, y);
820715
}
821716

822-
void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer, bool fallback = false)
717+
void FT2Font::get_glyph_name(unsigned int glyph_number, std::string &buffer,
718+
bool fallback = false)
823719
{
824720
if (fallback && glyph_to_font.find(glyph_number) != glyph_to_font.end()) {
825721
// cache is only for parent FT2Font
@@ -830,9 +726,11 @@ void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer, bool fallb
830726
if (!FT_HAS_GLYPH_NAMES(face)) {
831727
/* Note that this generated name must match the name that
832728
is generated by ttconv in ttfont_CharStrings_getname. */
833-
PyOS_snprintf(buffer, 128, "uni%08x", glyph_number);
729+
buffer.replace(0, 3, "uni");
730+
std::to_chars(buffer.data() + 3, buffer.data() + buffer.size(),
731+
glyph_number, 16);
834732
} else {
835-
if (FT_Error error = FT_Get_Glyph_Name(face, glyph_number, buffer, 128)) {
733+
if (FT_Error error = FT_Get_Glyph_Name(face, glyph_number, buffer.data(), buffer.size())) {
836734
throw_ft_error("Could not get glyph names", error);
837735
}
838736
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy