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
7270FT2Image::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
111107void 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
171144void
@@ -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.
218160struct 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
225166static int
226167ft_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
244182static int
245183ft_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
257192static int
258193ft_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
341258FT2Font::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-
797692void 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