@@ -551,73 +551,25 @@ font_getlength(FontObject *self, PyObject *args) {
551551 return PyLong_FromLong (length );
552552}
553553
554- static PyObject *
555- font_getsize ( FontObject * self , PyObject * args ) {
554+ static int
555+ bounding_box_and_anchors ( FT_Face face , const char * anchor , int horizontal_dir , GlyphInfo * glyph_info , size_t count , int load_flags , int * width , int * height , int * x_offset , int * y_offset ) {
556556 int position ; /* pen position along primary axis, in 26.6 precision */
557557 int advanced ; /* pen position along primary axis, in pixels */
558558 int px , py ; /* position of current glyph, in pixels */
559559 int x_min , x_max , y_min , y_max ; /* text bounding box, in pixels */
560560 int x_anchor , y_anchor ; /* offset of point drawn at (0, 0), in pixels */
561- int load_flags ; /* FreeType load_flags parameter */
562561 int error ;
563- FT_Face face ;
564562 FT_Glyph glyph ;
565- FT_BBox bbox ; /* glyph bounding box */
566- GlyphInfo * glyph_info = NULL ; /* computed text layout */
567- size_t i , count ; /* glyph_info index and length */
568- int horizontal_dir ; /* is primary axis horizontal? */
569- int mask = 0 ; /* is FT_LOAD_TARGET_MONO enabled? */
570- int color = 0 ; /* is FT_LOAD_COLOR enabled? */
571- const char * mode = NULL ;
572- const char * dir = NULL ;
573- const char * lang = NULL ;
574- const char * anchor = NULL ;
575- PyObject * features = Py_None ;
576- PyObject * string ;
577-
578- /* calculate size and bearing for a given string */
579-
580- if (!PyArg_ParseTuple (
581- args , "O|zzOzz:getsize" , & string , & mode , & dir , & features , & lang , & anchor )) {
582- return NULL ;
583- }
584-
585- horizontal_dir = dir && strcmp (dir , "ttb" ) == 0 ? 0 : 1 ;
586-
587- mask = mode && strcmp (mode , "1" ) == 0 ;
588- color = mode && strcmp (mode , "RGBA" ) == 0 ;
589-
590- if (anchor == NULL ) {
591- anchor = horizontal_dir ? "la" : "lt" ;
592- }
593- if (strlen (anchor ) != 2 ) {
594- goto bad_anchor ;
595- }
596-
597- count = text_layout (string , self , dir , features , lang , & glyph_info , mask , color );
598- if (PyErr_Occurred ()) {
599- return NULL ;
600- }
601-
602- load_flags = FT_LOAD_DEFAULT ;
603- if (mask ) {
604- load_flags |= FT_LOAD_TARGET_MONO ;
605- }
606- if (color ) {
607- load_flags |= FT_LOAD_COLOR ;
608- }
609-
563+ FT_BBox bbox ; /* glyph bounding box */
564+ size_t i ; /* glyph_info index */
610565 /*
611566 * text bounds are given by:
612567 * - bounding boxes of individual glyphs
613568 * - pen line, i.e. 0 to `advanced` along primary axis
614569 * this means point (0, 0) is part of the text bounding box
615570 */
616- face = NULL ;
617571 position = x_min = x_max = y_min = y_max = 0 ;
618572 for (i = 0 ; i < count ; i ++ ) {
619- face = self -> face ;
620-
621573 if (horizontal_dir ) {
622574 px = PIXEL (position + glyph_info [i ].x_offset );
623575 py = PIXEL (glyph_info [i ].y_offset );
@@ -640,12 +592,14 @@ font_getsize(FontObject *self, PyObject *args) {
640592
641593 error = FT_Load_Glyph (face , glyph_info [i ].index , load_flags );
642594 if (error ) {
643- return geterror (error );
595+ geterror (error );
596+ return 1 ;
644597 }
645598
646599 error = FT_Get_Glyph (face -> glyph , & glyph );
647600 if (error ) {
648- return geterror (error );
601+ geterror (error );
602+ return 1 ;
649603 }
650604
651605 FT_Glyph_Get_CBox (glyph , FT_GLYPH_BBOX_PIXELS , & bbox );
@@ -669,13 +623,15 @@ font_getsize(FontObject *self, PyObject *args) {
669623 FT_Done_Glyph (glyph );
670624 }
671625
672- if (glyph_info ) {
673- PyMem_Free (glyph_info );
674- glyph_info = NULL ;
626+ if (anchor == NULL ) {
627+ anchor = horizontal_dir ? "la" : "lt" ;
628+ }
629+ if (strlen (anchor ) != 2 ) {
630+ goto bad_anchor ;
675631 }
676632
677633 x_anchor = y_anchor = 0 ;
678- if (face ) {
634+ if (count ) {
679635 if (horizontal_dir ) {
680636 switch (anchor [0 ]) {
681637 case 'l' : // left
@@ -693,15 +649,15 @@ font_getsize(FontObject *self, PyObject *args) {
693649 }
694650 switch (anchor [1 ]) {
695651 case 'a' : // ascender
696- y_anchor = PIXEL (self -> face -> size -> metrics .ascender );
652+ y_anchor = PIXEL (face -> size -> metrics .ascender );
697653 break ;
698654 case 't' : // top
699655 y_anchor = y_max ;
700656 break ;
701657 case 'm' : // middle (ascender + descender) / 2
702658 y_anchor = PIXEL (
703- (self -> face -> size -> metrics .ascender +
704- self -> face -> size -> metrics .descender ) /
659+ (face -> size -> metrics .ascender +
660+ face -> size -> metrics .descender ) /
705661 2 );
706662 break ;
707663 case 's' : // horizontal baseline
@@ -711,7 +667,7 @@ font_getsize(FontObject *self, PyObject *args) {
711667 y_anchor = y_min ;
712668 break ;
713669 case 'd' : // descender
714- y_anchor = PIXEL (self -> face -> size -> metrics .descender );
670+ y_anchor = PIXEL (face -> size -> metrics .descender );
715671 break ;
716672 default :
717673 goto bad_anchor ;
@@ -751,17 +707,74 @@ font_getsize(FontObject *self, PyObject *args) {
751707 }
752708 }
753709 }
754-
755- return Py_BuildValue (
756- "(ii)(ii)" ,
757- (x_max - x_min ),
758- (y_max - y_min ),
759- (- x_anchor + x_min ),
760- - (- y_anchor + y_max ));
710+ * width = x_max - x_min ;
711+ * height = y_max - y_min ;
712+ * x_offset = - x_anchor + x_min ;
713+ * y_offset = - (- y_anchor + y_max );
714+ return 0 ;
761715
762716bad_anchor :
763717 PyErr_Format (PyExc_ValueError , "bad anchor specified: %s" , anchor );
764- return NULL ;
718+ return 1 ;
719+ }
720+
721+ static PyObject *
722+ font_getsize (FontObject * self , PyObject * args ) {
723+ int width , height , x_offset , y_offset ;
724+ int load_flags ; /* FreeType load_flags parameter */
725+ int error ;
726+ GlyphInfo * glyph_info = NULL ; /* computed text layout */
727+ size_t count ; /* glyph_info length */
728+ int horizontal_dir ; /* is primary axis horizontal? */
729+ int mask = 0 ; /* is FT_LOAD_TARGET_MONO enabled? */
730+ int color = 0 ; /* is FT_LOAD_COLOR enabled? */
731+ const char * mode = NULL ;
732+ const char * dir = NULL ;
733+ const char * lang = NULL ;
734+ const char * anchor = NULL ;
735+ PyObject * features = Py_None ;
736+ PyObject * string ;
737+
738+ /* calculate size and bearing for a given string */
739+
740+ if (!PyArg_ParseTuple (
741+ args , "O|zzOzz:getsize" , & string , & mode , & dir , & features , & lang , & anchor )) {
742+ return NULL ;
743+ }
744+
745+ horizontal_dir = dir && strcmp (dir , "ttb" ) == 0 ? 0 : 1 ;
746+
747+ mask = mode && strcmp (mode , "1" ) == 0 ;
748+ color = mode && strcmp (mode , "RGBA" ) == 0 ;
749+
750+ count = text_layout (string , self , dir , features , lang , & glyph_info , mask , color );
751+ if (PyErr_Occurred ()) {
752+ return NULL ;
753+ }
754+
755+ load_flags = FT_LOAD_DEFAULT ;
756+ if (mask ) {
757+ load_flags |= FT_LOAD_TARGET_MONO ;
758+ }
759+ if (color ) {
760+ load_flags |= FT_LOAD_COLOR ;
761+ }
762+
763+ error = bounding_box_and_anchors (self -> face , anchor , horizontal_dir , glyph_info , count , load_flags , & width , & height , & x_offset , & y_offset );
764+ if (glyph_info ) {
765+ PyMem_Free (glyph_info );
766+ glyph_info = NULL ;
767+ }
768+ if (error ) {
769+ return NULL ;
770+ }
771+
772+ return Py_BuildValue (
773+ "(ii)(ii)" ,
774+ width ,
775+ height ,
776+ x_offset ,
777+ y_offset );
765778}
766779
767780static PyObject *
@@ -785,6 +798,7 @@ font_render(FontObject *self, PyObject *args) {
785798 unsigned int bitmap_y ; /* glyph bitmap y index */
786799 unsigned char * source ; /* glyph bitmap source buffer */
787800 unsigned char convert_scale ; /* scale factor for non-8bpp bitmaps */
801+ PyObject * image ;
788802 Imaging im ;
789803 Py_ssize_t id ;
790804 int mask = 0 ; /* is FT_LOAD_TARGET_MONO enabled? */
@@ -795,27 +809,34 @@ font_render(FontObject *self, PyObject *args) {
795809 const char * mode = NULL ;
796810 const char * dir = NULL ;
797811 const char * lang = NULL ;
812+ const char * anchor = NULL ;
798813 PyObject * features = Py_None ;
799814 PyObject * string ;
815+ PyObject * fill ;
800816 float x_start = 0 ;
801817 float y_start = 0 ;
818+ int width , height , x_offset , y_offset ;
819+ int horizontal_dir ; /* is primary axis horizontal? */
820+ PyObject * max_image_pixels = Py_None ;
802821
803822 /* render string into given buffer (the buffer *must* have
804823 the right size, or this will crash) */
805824
806825 if (!PyArg_ParseTuple (
807826 args ,
808- "On|zzOziLff :render" ,
827+ "OO|zzOzizLffO :render" ,
809828 & string ,
810- & id ,
829+ & fill ,
811830 & mode ,
812831 & dir ,
813832 & features ,
814833 & lang ,
815834 & stroke_width ,
835+ & anchor ,
816836 & foreground_ink_long ,
817837 & x_start ,
818- & y_start )) {
838+ & y_start ,
839+ & max_image_pixels )) {
819840 return NULL ;
820841 }
821842
@@ -841,8 +862,41 @@ font_render(FontObject *self, PyObject *args) {
841862 if (PyErr_Occurred ()) {
842863 return NULL ;
843864 }
844- if (count == 0 ) {
845- Py_RETURN_NONE ;
865+
866+ load_flags = stroke_width ? FT_LOAD_NO_BITMAP : FT_LOAD_DEFAULT ;
867+ if (mask ) {
868+ load_flags |= FT_LOAD_TARGET_MONO ;
869+ }
870+ if (color ) {
871+ load_flags |= FT_LOAD_COLOR ;
872+ }
873+
874+ horizontal_dir = dir && strcmp (dir , "ttb" ) == 0 ? 0 : 1 ;
875+
876+ error = bounding_box_and_anchors (self -> face , anchor , horizontal_dir , glyph_info , count , load_flags , & width , & height , & x_offset , & y_offset );
877+ if (error ) {
878+ PyMem_Del (glyph_info );
879+ return NULL ;
880+ }
881+
882+ width += stroke_width * 2 + ceil (x_start );
883+ height += stroke_width * 2 + ceil (y_start );
884+ if (max_image_pixels != Py_None ) {
885+ if (width * height > PyLong_AsLong (max_image_pixels ) * 2 ) {
886+ PyMem_Del (glyph_info );
887+ return Py_BuildValue ("O(ii)(ii)" , Py_None , width , height , 0 , 0 );
888+ }
889+ }
890+
891+ image = PyObject_CallFunction (fill , "s(ii)" , strcmp (mode , "RGBA" ) == 0 ? "RGBA" : "L" , width , height );
892+ id = PyLong_AsSsize_t (PyObject_GetAttrString (image , "id" ));
893+ im = (Imaging )id ;
894+
895+ x_offset -= stroke_width ;
896+ y_offset -= stroke_width ;
897+ if (count == 0 || width == 0 || height == 0 ) {
898+ PyMem_Del (glyph_info );
899+ return Py_BuildValue ("O(ii)(ii)" , image , width , height , x_offset , y_offset );
846900 }
847901
848902 if (stroke_width ) {
@@ -859,15 +913,6 @@ font_render(FontObject *self, PyObject *args) {
859913 0 );
860914 }
861915
862- im = (Imaging )id ;
863- load_flags = stroke_width ? FT_LOAD_NO_BITMAP : FT_LOAD_DEFAULT ;
864- if (mask ) {
865- load_flags |= FT_LOAD_TARGET_MONO ;
866- }
867- if (color ) {
868- load_flags |= FT_LOAD_COLOR ;
869- }
870-
871916 /*
872917 * calculate x_min and y_max
873918 * must match font_getsize or there may be clipping!
@@ -1064,7 +1109,7 @@ font_render(FontObject *self, PyObject *args) {
10641109 }
10651110 FT_Stroker_Done (stroker );
10661111 PyMem_Del (glyph_info );
1067- Py_RETURN_NONE ;
1112+ return Py_BuildValue ( "O(ii)(ii)" , image , width , height , x_offset , y_offset ) ;
10681113
10691114glyph_error :
10701115 if (stroker != NULL ) {
0 commit comments