1
1
/* -*- mode: c++; c-basic-offset: 4 -*- */
2
2
3
- #define NO_IMPORT_ARRAY
4
-
5
3
#include < algorithm>
4
+ #include < charconv>
6
5
#include < iterator>
7
6
#include < set>
8
7
#include < sstream>
9
8
#include < stdexcept>
10
9
#include < string>
10
+ #include < vector>
11
11
12
12
#include " ft2font.h"
13
13
#include " mplutils.h"
14
- #include " numpy_cpp.h"
15
- #include " py_exceptions.h"
16
14
17
15
#ifndef M_PI
18
16
#define M_PI 3.14159265358979323846264338328
@@ -65,12 +63,12 @@ void throw_ft_error(std::string message, FT_Error error) {
65
63
throw std::runtime_error (os.str ());
66
64
}
67
65
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 )
69
67
{
70
68
}
71
69
72
70
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 )
74
72
{
75
73
resize (width, height);
76
74
}
@@ -104,8 +102,6 @@ void FT2Image::resize(long width, long height)
104
102
if (numBytes && m_buffer) {
105
103
memset (m_buffer, 0 , numBytes);
106
104
}
107
-
108
- m_dirty = true ;
109
105
}
110
106
111
107
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)
143
139
} else {
144
140
throw std::runtime_error (" Unknown pixel mode" );
145
141
}
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 ;
169
142
}
170
143
171
144
void
@@ -181,92 +154,51 @@ FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1,
181
154
m_buffer[i + j * m_width] = 255 ;
182
155
}
183
156
}
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
- }
210
157
}
211
158
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.
218
160
struct ft_outline_decomposer
219
161
{
220
- int index;
221
- double * vertices;
222
- unsigned char * codes;
162
+ std::vector<double > &vertices;
163
+ std::vector<unsigned char > &codes;
223
164
};
224
165
225
166
static int
226
167
ft_outline_move_to (FT_Vector const * to, void * user)
227
168
{
228
169
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);
241
179
return 0 ;
242
180
}
243
181
244
182
static int
245
183
ft_outline_line_to (FT_Vector const * to, void * user)
246
184
{
247
185
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);
254
189
return 0 ;
255
190
}
256
191
257
192
static int
258
193
ft_outline_conic_to (FT_Vector const * control, FT_Vector const * to, void * user)
259
194
{
260
195
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);
270
202
return 0 ;
271
203
}
272
204
@@ -275,18 +207,15 @@ ft_outline_cubic_to(
275
207
FT_Vector const * c1, FT_Vector const * c2, FT_Vector const * to, void * user)
276
208
{
277
209
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);
290
219
return 0 ;
291
220
}
292
221
@@ -296,52 +225,41 @@ static FT_Outline_Funcs ft_outline_funcs = {
296
225
ft_outline_conic_to,
297
226
ft_outline_cubic_to};
298
227
299
- PyObject*
300
- FT2Font::get_path ()
228
+ void
229
+ FT2Font::get_path (std::vector< double > &vertices, std::vector< unsigned char > &codes )
301
230
{
302
231
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);
339
256
}
340
257
341
258
FT2Font::FT2Font (FT_Open_Args &open_args,
342
259
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 )
345
263
{
346
264
clear ();
347
265
@@ -771,29 +689,6 @@ void FT2Font::draw_glyphs_to_bitmap(bool antialiased)
771
689
}
772
690
}
773
691
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
-
797
692
void FT2Font::draw_glyph_to_bitmap (FT2Image &im, int x, int y, size_t glyphInd, bool antialiased)
798
693
{
799
694
FT_Vector sub_offset;
@@ -819,7 +714,8 @@ void FT2Font::draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd,
819
714
im.draw_bitmap (&bitmap->bitmap , x + bitmap->left , y);
820
715
}
821
716
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 )
823
719
{
824
720
if (fallback && glyph_to_font.find (glyph_number) != glyph_to_font.end ()) {
825
721
// cache is only for parent FT2Font
@@ -830,9 +726,11 @@ void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer, bool fallb
830
726
if (!FT_HAS_GLYPH_NAMES (face)) {
831
727
/* Note that this generated name must match the name that
832
728
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 );
834
732
} 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 () )) {
836
734
throw_ft_error (" Could not get glyph names" , error);
837
735
}
838
736
}
0 commit comments