mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-31 07:57:27 +03:00 
			
		
		
		
	fix text clipping due to rounding
(cherry picked from commit 35500aad08fb18a904f326df2ce8c59ae6413801)
This commit is contained in:
		
							parent
							
								
									e3450d1f6e
								
							
						
					
					
						commit
						5d57261b9c
					
				
							
								
								
									
										199
									
								
								src/_imagingft.c
									
									
									
									
									
								
							
							
						
						
									
										199
									
								
								src/_imagingft.c
									
									
									
									
									
								
							|  | @ -612,7 +612,7 @@ font_getsize(FontObject* self, PyObject* args) | ||||||
|     int position, advanced; |     int position, advanced; | ||||||
|     int x_max, x_min, y_max, y_min; |     int x_max, x_min, y_max, y_min; | ||||||
|     FT_Face face; |     FT_Face face; | ||||||
|     int xoffset, yoffset; |     int x_anchor, y_anchor; | ||||||
|     int horizontal_dir; |     int horizontal_dir; | ||||||
|     int mask = 0; |     int mask = 0; | ||||||
|     int load_flags; |     int load_flags; | ||||||
|  | @ -621,6 +621,7 @@ font_getsize(FontObject* self, PyObject* args) | ||||||
|     size_t i, count; |     size_t i, count; | ||||||
|     GlyphInfo *glyph_info = NULL; |     GlyphInfo *glyph_info = NULL; | ||||||
|     PyObject *features = Py_None; |     PyObject *features = Py_None; | ||||||
|  |     FT_Vector pen; | ||||||
| 
 | 
 | ||||||
|     /* calculate size and bearing for a given string */ |     /* calculate size and bearing for a given string */ | ||||||
| 
 | 
 | ||||||
|  | @ -643,6 +644,29 @@ font_getsize(FontObject* self, PyObject* args) | ||||||
|         FT_Glyph glyph; |         FT_Glyph glyph; | ||||||
|         face = self->face; |         face = self->face; | ||||||
|         index = glyph_info[i].index; |         index = glyph_info[i].index; | ||||||
|  | 
 | ||||||
|  |         if (horizontal_dir) { | ||||||
|  |             pen.x = position + glyph_info[i].x_offset; | ||||||
|  |             pen.y = glyph_info[i].y_offset; | ||||||
|  | 
 | ||||||
|  |             position += glyph_info[i].x_advance; | ||||||
|  |             advanced = PIXEL(position); | ||||||
|  |             if (advanced > x_max) { | ||||||
|  |                 x_max = advanced; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             pen.x = glyph_info[i].x_offset; | ||||||
|  |             pen.y = position + glyph_info[i].y_offset; | ||||||
|  | 
 | ||||||
|  |             position += glyph_info[i].y_advance; | ||||||
|  |             advanced = PIXEL(position); | ||||||
|  |             if (advanced < y_min) { | ||||||
|  |                 y_min = advanced; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         FT_Set_Transform(face, NULL, &pen); | ||||||
|  | 
 | ||||||
|         /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960
 |         /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960
 | ||||||
|          *   Yifu Yu<root@jackyyf.com>, 2014-10-15 |          *   Yifu Yu<root@jackyyf.com>, 2014-10-15 | ||||||
|          */ |          */ | ||||||
|  | @ -655,74 +679,24 @@ font_getsize(FontObject* self, PyObject* args) | ||||||
|             return geterror(error); |             return geterror(error); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         FT_Get_Glyph(face->glyph, &glyph); |         error = FT_Get_Glyph(face->glyph, &glyph); | ||||||
|         FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_SUBPIXELS, &bbox); |         if (error) { | ||||||
|         if (horizontal_dir) { |             return geterror(error); | ||||||
|             // adjust start position if glyph bearing extends before origin
 |         } | ||||||
|             offset = position + face->glyph->metrics.horiBearingX + glyph_info[i].x_offset; |  | ||||||
|             if (offset < x_min) { |  | ||||||
|                 x_min = offset; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             position += glyph_info[i].x_advance; |  | ||||||
|             advanced = position; |  | ||||||
| 
 | 
 | ||||||
|             // adjust glyph end position if bearing extends past advanced point
 |         FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox); | ||||||
|             offset = glyph_info[i].x_advance - |  | ||||||
|                     glyph_info[i].x_offset - |  | ||||||
|                     face->glyph->metrics.horiBearingX - |  | ||||||
|                     face->glyph->metrics.width; |  | ||||||
|             if (offset < 0) { |  | ||||||
|                 advanced -= offset; |  | ||||||
|             } |  | ||||||
|             if (advanced > x_max) { |  | ||||||
|                 x_max = advanced; |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             bbox.yMax += glyph_info[i].y_offset; |         if (bbox.xMax > x_max) { | ||||||
|             bbox.yMin += glyph_info[i].y_offset; |             x_max = bbox.xMax; | ||||||
|             if (bbox.yMax > y_max) { |         } | ||||||
|                 y_max = bbox.yMax; |         if (bbox.xMin < x_min) { | ||||||
|             } |             x_min = bbox.xMin; | ||||||
|             if (bbox.yMin < y_min) { |         } | ||||||
|                 y_min = bbox.yMin; |         if (bbox.yMax > y_max) { | ||||||
|             } |             y_max = bbox.yMax; | ||||||
|         } else { |         } | ||||||
|             // NOTE: harfbuzz (called from raqm) assumes that we are using horizontal
 |         if (bbox.yMin < y_min) { | ||||||
|             // bearings even for vertical text and adjusts offsets to compensate as follows:
 |             y_min = bbox.yMin; | ||||||
|             //   hb-ft.cc: hb_ft_get_glyph_v_origin():
 |  | ||||||
|             //     x_offset += horiBearingX - vertBearingX;
 |  | ||||||
|             //     y_offset += horiBearingY - (-vertBearingY);
 |  | ||||||
| 
 |  | ||||||
|             // adjust start position if glyph bearing extends before origin
 |  | ||||||
|             offset = position + face->glyph->metrics.horiBearingY + glyph_info[i].y_offset; |  | ||||||
|             if (offset > y_max) { |  | ||||||
|                 y_max = offset; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             position += glyph_info[i].y_advance; |  | ||||||
|             advanced = position; |  | ||||||
| 
 |  | ||||||
|             // adjust glyph end position if bearing extends past advanced point
 |  | ||||||
|             offset = glyph_info[i].y_advance - |  | ||||||
|                 glyph_info[i].y_offset -     |  | ||||||
|                 face->glyph->metrics.horiBearingY + |  | ||||||
|                 face->glyph->metrics.height; |  | ||||||
|             if (offset > 0) { |  | ||||||
|                 advanced -= offset; |  | ||||||
|             } |  | ||||||
|             if (advanced < y_min) { |  | ||||||
|                 y_min = advanced; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             bbox.xMax += glyph_info[i].x_offset; |  | ||||||
|             bbox.xMin += glyph_info[i].x_offset; |  | ||||||
|             if (bbox.xMax > x_max) { |  | ||||||
|                 x_max = bbox.xMax; |  | ||||||
|             } |  | ||||||
|             if (bbox.xMin < x_min) { |  | ||||||
|                 x_min = bbox.xMin; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         FT_Done_Glyph(glyph); |         FT_Done_Glyph(glyph); | ||||||
|  | @ -733,21 +707,23 @@ font_getsize(FontObject* self, PyObject* args) | ||||||
|         glyph_info = NULL; |         glyph_info = NULL; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     x_anchor = y_anchor = 0; | ||||||
|     if (face) { |     if (face) { | ||||||
|  |         FT_Set_Transform(face, NULL, NULL); | ||||||
|         if (horizontal_dir) { |         if (horizontal_dir) { | ||||||
|             xoffset = 0; |             x_anchor = 0; | ||||||
|             yoffset = self->face->size->metrics.ascender - y_max; |             y_anchor = PIXEL(self->face->size->metrics.ascender); | ||||||
|         } else { |         } else { | ||||||
|             xoffset = 0; |             x_anchor = x_min; | ||||||
|             yoffset = -y_max; |             y_anchor = 0; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return Py_BuildValue( |     return Py_BuildValue( | ||||||
|         "(ii)(ii)", |         "(ii)(ii)", | ||||||
|         PIXEL(x_max - x_min), PIXEL(y_max - y_min), |         (x_max - x_min), (y_max - y_min), | ||||||
|         PIXEL(xoffset), PIXEL(yoffset) |         (-x_anchor + x_min), -(-y_anchor + y_max) | ||||||
|         ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static PyObject* | static PyObject* | ||||||
|  | @ -777,6 +753,7 @@ font_render(FontObject* self, PyObject* args) | ||||||
|     size_t i, count; |     size_t i, count; | ||||||
|     GlyphInfo *glyph_info; |     GlyphInfo *glyph_info; | ||||||
|     PyObject *features = NULL; |     PyObject *features = NULL; | ||||||
|  |     FT_Vector pen; | ||||||
| 
 | 
 | ||||||
|     if (!PyArg_ParseTuple(args, "On|izOzi:render", &string,  &id, &mask, &dir, &features, &lang, |     if (!PyArg_ParseTuple(args, "On|izOzi:render", &string,  &id, &mask, &dir, &features, &lang, | ||||||
|                                                    &stroke_width)) { |                                                    &stroke_width)) { | ||||||
|  | @ -808,10 +785,15 @@ font_render(FontObject* self, PyObject* args) | ||||||
|         load_flags |= FT_LOAD_TARGET_MONO; |         load_flags |= FT_LOAD_TARGET_MONO; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     x = y = baseline = offset = 0; |     pen.x = pen.y = x = y = baseline = offset = 0; | ||||||
|     horizontal_dir = (dir && strcmp(dir, "ttb") == 0) ? 0 : 1; |     horizontal_dir = (dir && strcmp(dir, "ttb") == 0) ? 0 : 1; | ||||||
|     for (i = 0; i < count; i++) { |     for (i = 0; i < count; i++) { | ||||||
|         index = glyph_info[i].index; |         index = glyph_info[i].index; | ||||||
|  | 
 | ||||||
|  |         pen.x = x + glyph_info[i].x_offset; | ||||||
|  |         pen.y = y + glyph_info[i].y_offset; | ||||||
|  |         FT_Set_Transform(self->face, NULL, &pen); | ||||||
|  | 
 | ||||||
|         error = FT_Load_Glyph(self->face, index, load_flags | FT_LOAD_RENDER); |         error = FT_Load_Glyph(self->face, index, load_flags | FT_LOAD_RENDER); | ||||||
|         if (error) { |         if (error) { | ||||||
|             return geterror(error); |             return geterror(error); | ||||||
|  | @ -822,40 +804,30 @@ font_render(FontObject* self, PyObject* args) | ||||||
| 
 | 
 | ||||||
|         // compute baseline and adjust start position if glyph bearing extends before origin
 |         // compute baseline and adjust start position if glyph bearing extends before origin
 | ||||||
|         if (horizontal_dir) { |         if (horizontal_dir) { | ||||||
|             temp = glyph_slot->metrics.horiBearingY + glyph_info[i].y_offset; |             if (glyph_slot->bitmap_top > baseline) { | ||||||
|             if (temp > baseline) { |                 baseline = glyph_slot->bitmap_top; | ||||||
|                 baseline = temp; |  | ||||||
|             } |             } | ||||||
| 
 |             if (glyph_slot->bitmap_left < offset) { | ||||||
|             temp = x + glyph_slot->metrics.horiBearingX + glyph_info[i].x_offset; |                 offset = glyph_slot->bitmap_left; | ||||||
|             if (temp < offset) { |  | ||||||
|                 offset = temp; |  | ||||||
|             } |             } | ||||||
|             x += glyph_info[i].x_advance; |             x += glyph_info[i].x_advance; | ||||||
|         } else { |         } else { | ||||||
|             // NOTE: harfbuzz (called from raqm) assumes that we are using horizontal
 |             if (glyph_slot->bitmap_top > offset) { | ||||||
|             // bearings even for vertical text and adjusts offsets to compensate as follows:
 |                 offset = glyph_slot->bitmap_top; | ||||||
|             //   hb-ft.cc: hb_ft_get_glyph_v_origin():
 |  | ||||||
|             //     x_offset += horiBearingX - vertBearingX;
 |  | ||||||
|             //     y_offset += horiBearingY - (-vertBearingY);
 |  | ||||||
| 
 |  | ||||||
|             temp = -(glyph_slot->metrics.horiBearingX + glyph_info[i].x_offset); |  | ||||||
|             if (temp > baseline) { |  | ||||||
|                 baseline = temp; |  | ||||||
|             } |             } | ||||||
| 
 |             if (glyph_slot->bitmap_left < baseline) { | ||||||
|             temp = y + glyph_slot->metrics.horiBearingY + glyph_info[i].y_offset; |                 baseline = glyph_slot->bitmap_left; | ||||||
|             if (temp > offset) { |  | ||||||
|                 offset = temp; |  | ||||||
|             } |             } | ||||||
|             y += glyph_info[i].y_advance; |             y += glyph_info[i].y_advance; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (horizontal_dir) { |     if (horizontal_dir) { | ||||||
|         x = -offset; |         x = (-offset + stroke_width) * 64; | ||||||
|  |         y = (-baseline + (-stroke_width)) * 64; | ||||||
|     } else { |     } else { | ||||||
|         y = -offset; |         x = (-baseline + stroke_width) * 64; | ||||||
|  |         y = (-offset + (-stroke_width)) * 64; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (stroker == NULL) { |     if (stroker == NULL) { | ||||||
|  | @ -864,6 +836,11 @@ font_render(FontObject* self, PyObject* args) | ||||||
| 
 | 
 | ||||||
|     for (i = 0; i < count; i++) { |     for (i = 0; i < count; i++) { | ||||||
|         index = glyph_info[i].index; |         index = glyph_info[i].index; | ||||||
|  | 
 | ||||||
|  |         pen.x = x + glyph_info[i].x_offset; | ||||||
|  |         pen.y = y + glyph_info[i].y_offset; | ||||||
|  |         FT_Set_Transform(self->face, NULL, &pen); | ||||||
|  | 
 | ||||||
|         error = FT_Load_Glyph(self->face, index, load_flags); |         error = FT_Load_Glyph(self->face, index, load_flags); | ||||||
|         if (error) { |         if (error) { | ||||||
|             return geterror(error); |             return geterror(error); | ||||||
|  | @ -886,19 +863,12 @@ font_render(FontObject* self, PyObject* args) | ||||||
|             bitmap_glyph = (FT_BitmapGlyph)glyph; |             bitmap_glyph = (FT_BitmapGlyph)glyph; | ||||||
| 
 | 
 | ||||||
|             bitmap = bitmap_glyph->bitmap; |             bitmap = bitmap_glyph->bitmap; | ||||||
|  |             xx = bitmap_glyph->left; | ||||||
|  |             yy = -bitmap_glyph->top; | ||||||
|         } else { |         } else { | ||||||
|             bitmap = glyph_slot->bitmap; |             bitmap = glyph_slot->bitmap; | ||||||
|         } |             xx = glyph_slot->bitmap_left; | ||||||
| 
 |             yy = -glyph_slot->bitmap_top; | ||||||
|         // NOTE: harfbuzz (called from raqm) assumes that we are using horizontal
 |  | ||||||
|         // bearings even for vertical text and adjusts offsets to compensate as follows:
 |  | ||||||
|         //   hb-ft.cc: hb_ft_get_glyph_v_origin():
 |  | ||||||
|         //     x_offset += horiBearingX - vertBearingX;
 |  | ||||||
|         //     y_offset += horiBearingY - (-vertBearingY);
 |  | ||||||
|         if (horizontal_dir) { |  | ||||||
|             xx = PIXEL(x + glyph_slot->metrics.horiBearingX + glyph_info[i].x_offset); |  | ||||||
|         } else { |  | ||||||
|             xx = PIXEL(baseline + glyph_slot->metrics.horiBearingX + glyph_info[i].x_offset); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         x0 = 0; |         x0 = 0; | ||||||
|  | @ -911,17 +881,7 @@ font_render(FontObject* self, PyObject* args) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         source = (unsigned char*) bitmap.buffer; |         source = (unsigned char*) bitmap.buffer; | ||||||
|         for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++) { |         for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) { | ||||||
|             // NOTE: harfbuzz (called from raqm) assumes that we are using horizontal
 |  | ||||||
|             // bearings even for vertical text and adjusts offsets to compensate as follows:
 |  | ||||||
|             //   hb-ft.cc: hb_ft_get_glyph_v_origin():
 |  | ||||||
|             //     x_offset += horiBearingX - vertBearingX;
 |  | ||||||
|             //     y_offset += horiBearingY - (-vertBearingY);
 |  | ||||||
|             if (horizontal_dir) { |  | ||||||
|                 yy = PIXEL(baseline - glyph_slot->metrics.horiBearingY - glyph_info[i].y_offset) + bitmap_y; |  | ||||||
|             } else { |  | ||||||
|                 yy = PIXEL(-(y + glyph_slot->metrics.horiBearingY + glyph_info[i].y_offset)) + bitmap_y; |  | ||||||
|             } |  | ||||||
|             if (yy >= 0 && yy < im->ysize) { |             if (yy >= 0 && yy < im->ysize) { | ||||||
|                 // blend this glyph into the buffer
 |                 // blend this glyph into the buffer
 | ||||||
|                 unsigned char *target = im->image8[yy] + xx; |                 unsigned char *target = im->image8[yy] + xx; | ||||||
|  | @ -956,6 +916,7 @@ font_render(FontObject* self, PyObject* args) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     FT_Set_Transform(self->face, NULL, NULL); | ||||||
|     FT_Stroker_Done(stroker); |     FT_Stroker_Done(stroker); | ||||||
|     PyMem_Del(glyph_info); |     PyMem_Del(glyph_info); | ||||||
|     Py_RETURN_NONE; |     Py_RETURN_NONE; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user