Skip to content

Commit f9f7ba4

Browse files
committed
Do not raise error if bitmap buffer is empty
1 parent d6cfebd commit f9f7ba4

2 files changed

Lines changed: 137 additions & 138 deletions

File tree

Tests/test_font_crash.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
from __future__ import annotations
22

3-
import pytest
4-
53
from PIL import Image, ImageDraw, ImageFont
64

7-
from .helper import skip_unless_feature
5+
from .helper import skip_unless_feature_version
86

97

108
class TestFontCrash:
@@ -17,8 +15,7 @@ def _fuzz_font(self, font: ImageFont.FreeTypeFont) -> None:
1715
draw.multiline_textbbox((10, 10), "ABC\nAaaa", font, stroke_width=2)
1816
draw.text((10, 10), "Test Text", font=font, fill="#000")
1917

20-
@skip_unless_feature("freetype2")
18+
@skip_unless_feature_version("freetype2", "2.12.0")
2119
def test_segfault(self) -> None:
22-
with pytest.raises(OSError):
23-
font = ImageFont.truetype("Tests/fonts/fuzz_font-5203009437302784")
24-
self._fuzz_font(font)
20+
font = ImageFont.truetype("Tests/fonts/fuzz_font-5203009437302784")
21+
self._fuzz_font(font)

src/_imagingft.c

Lines changed: 133 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,130 +1025,99 @@ font_render(FontObject *self, PyObject *args) {
10251025
yy = -(py + glyph_slot->bitmap_top);
10261026
}
10271027

1028-
// Null buffer, is dereferenced in FT_Bitmap_Convert
1029-
if (!bitmap.buffer && bitmap.rows) {
1030-
PyErr_SetString(PyExc_OSError, "Bitmap missing for glyph");
1031-
goto glyph_error;
1032-
}
1033-
1034-
/* convert non-8bpp bitmaps */
1035-
switch (bitmap.pixel_mode) {
1036-
case FT_PIXEL_MODE_MONO:
1037-
convert_scale = 255;
1038-
break;
1039-
case FT_PIXEL_MODE_GRAY2:
1040-
convert_scale = 255 / 3;
1041-
break;
1042-
case FT_PIXEL_MODE_GRAY4:
1043-
convert_scale = 255 / 15;
1044-
break;
1045-
default:
1046-
convert_scale = 1;
1047-
}
1048-
switch (bitmap.pixel_mode) {
1049-
case FT_PIXEL_MODE_MONO:
1050-
case FT_PIXEL_MODE_GRAY2:
1051-
case FT_PIXEL_MODE_GRAY4:
1052-
if (!bitmap_converted_ready) {
1053-
FT_Bitmap_Init(&bitmap_converted);
1054-
bitmap_converted_ready = 1;
1055-
}
1056-
error = FT_Bitmap_Convert(library, &bitmap, &bitmap_converted, 1);
1057-
if (error) {
1058-
geterror(error);
1059-
goto glyph_error;
1060-
}
1061-
bitmap = bitmap_converted;
1062-
/* bitmap is now FT_PIXEL_MODE_GRAY, fall through */
1063-
case FT_PIXEL_MODE_GRAY:
1064-
break;
1065-
case FT_PIXEL_MODE_BGRA:
1066-
if (color) {
1028+
if (bitmap.buffer) {
1029+
/* convert non-8bpp bitmaps */
1030+
switch (bitmap.pixel_mode) {
1031+
case FT_PIXEL_MODE_MONO:
1032+
convert_scale = 255;
10671033
break;
1068-
}
1069-
/* we didn't ask for color, fall through to default */
1070-
default:
1071-
PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode");
1072-
goto glyph_error;
1073-
}
1034+
case FT_PIXEL_MODE_GRAY2:
1035+
convert_scale = 255 / 3;
1036+
break;
1037+
case FT_PIXEL_MODE_GRAY4:
1038+
convert_scale = 255 / 15;
1039+
break;
1040+
default:
1041+
convert_scale = 1;
1042+
}
1043+
switch (bitmap.pixel_mode) {
1044+
case FT_PIXEL_MODE_MONO:
1045+
case FT_PIXEL_MODE_GRAY2:
1046+
case FT_PIXEL_MODE_GRAY4:
1047+
if (!bitmap_converted_ready) {
1048+
FT_Bitmap_Init(&bitmap_converted);
1049+
bitmap_converted_ready = 1;
1050+
}
1051+
error = FT_Bitmap_Convert(library, &bitmap, &bitmap_converted, 1);
1052+
if (error) {
1053+
geterror(error);
1054+
goto glyph_error;
1055+
}
1056+
bitmap = bitmap_converted;
1057+
/* bitmap is now FT_PIXEL_MODE_GRAY, fall through */
1058+
case FT_PIXEL_MODE_GRAY:
1059+
break;
1060+
case FT_PIXEL_MODE_BGRA:
1061+
if (color) {
1062+
break;
1063+
}
1064+
/* we didn't ask for color, fall through to default */
1065+
default:
1066+
PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode");
1067+
goto glyph_error;
1068+
}
10741069

1075-
/* clip glyph bitmap width to target image bounds */
1076-
x0 = 0;
1077-
x1 = bitmap.width;
1078-
if (xx < 0) {
1079-
x0 = -xx;
1080-
}
1081-
if (xx + x1 > im->xsize) {
1082-
x1 = im->xsize - xx;
1083-
}
1070+
/* clip glyph bitmap width to target image bounds */
1071+
x0 = 0;
1072+
x1 = bitmap.width;
1073+
if (xx < 0) {
1074+
x0 = -xx;
1075+
}
1076+
if (xx + x1 > im->xsize) {
1077+
x1 = im->xsize - xx;
1078+
}
10841079

1085-
source = (unsigned char *)bitmap.buffer;
1086-
for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) {
1087-
/* clip glyph bitmap height to target image bounds */
1088-
if (yy >= 0 && yy < im->ysize) {
1089-
/* blend this glyph into the buffer */
1090-
int k;
1091-
unsigned char *target;
1092-
unsigned int tmp;
1093-
if (color) {
1094-
/* target[RGB] returns the color, target[A] returns the mask */
1095-
/* target bands get split again in ImageDraw.text */
1096-
target = (unsigned char *)im->image[yy] + xx * 4;
1097-
} else {
1098-
target = im->image8[yy] + xx;
1099-
}
1100-
if (color && bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
1101-
/* paste color glyph */
1102-
for (k = x0; k < x1; k++) {
1103-
unsigned int src_alpha = source[k * 4 + 3];
1104-
1105-
/* paste only if source has data */
1106-
if (src_alpha > 0) {
1107-
/* unpremultiply BGRa */
1108-
int src_red =
1109-
CLIP8((255 * (int)source[k * 4 + 2]) / src_alpha);
1110-
int src_green =
1111-
CLIP8((255 * (int)source[k * 4 + 1]) / src_alpha);
1112-
int src_blue =
1113-
CLIP8((255 * (int)source[k * 4 + 0]) / src_alpha);
1114-
1115-
/* blend required if target has data */
1116-
if (target[k * 4 + 3] > 0) {
1117-
/* blend RGBA colors */
1118-
target[k * 4 + 0] =
1119-
BLEND(src_alpha, target[k * 4 + 0], src_red, tmp);
1120-
target[k * 4 + 1] =
1121-
BLEND(src_alpha, target[k * 4 + 1], src_green, tmp);
1122-
target[k * 4 + 2] =
1123-
BLEND(src_alpha, target[k * 4 + 2], src_blue, tmp);
1124-
target[k * 4 + 3] = CLIP8(
1125-
src_alpha +
1126-
MULDIV255(target[k * 4 + 3], (255 - src_alpha), tmp)
1127-
);
1128-
} else {
1129-
/* paste unpremultiplied RGBA values */
1130-
target[k * 4 + 0] = src_red;
1131-
target[k * 4 + 1] = src_green;
1132-
target[k * 4 + 2] = src_blue;
1133-
target[k * 4 + 3] = src_alpha;
1134-
}
1135-
}
1136-
}
1137-
} else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
1080+
source = (unsigned char *)bitmap.buffer;
1081+
for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) {
1082+
/* clip glyph bitmap height to target image bounds */
1083+
if (yy >= 0 && yy < im->ysize) {
1084+
/* blend this glyph into the buffer */
1085+
int k;
1086+
unsigned char *target;
1087+
unsigned int tmp;
11381088
if (color) {
1139-
unsigned char *ink = (unsigned char *)&foreground_ink;
1089+
/* target[RGB] returns the color, target[A] returns the mask */
1090+
/* target bands get split again in ImageDraw.text */
1091+
target = (unsigned char *)im->image[yy] + xx * 4;
1092+
} else {
1093+
target = im->image8[yy] + xx;
1094+
}
1095+
if (color && bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
1096+
/* paste color glyph */
11401097
for (k = x0; k < x1; k++) {
1141-
unsigned int src_alpha = source[k] * convert_scale;
1098+
unsigned int src_alpha = source[k * 4 + 3];
1099+
1100+
/* paste only if source has data */
11421101
if (src_alpha > 0) {
1102+
/* unpremultiply BGRa */
1103+
int src_red =
1104+
CLIP8((255 * (int)source[k * 4 + 2]) / src_alpha);
1105+
int src_green =
1106+
CLIP8((255 * (int)source[k * 4 + 1]) / src_alpha);
1107+
int src_blue =
1108+
CLIP8((255 * (int)source[k * 4 + 0]) / src_alpha);
1109+
1110+
/* blend required if target has data */
11431111
if (target[k * 4 + 3] > 0) {
1112+
/* blend RGBA colors */
11441113
target[k * 4 + 0] = BLEND(
1145-
src_alpha, target[k * 4 + 0], ink[0], tmp
1114+
src_alpha, target[k * 4 + 0], src_red, tmp
11461115
);
11471116
target[k * 4 + 1] = BLEND(
1148-
src_alpha, target[k * 4 + 1], ink[1], tmp
1117+
src_alpha, target[k * 4 + 1], src_green, tmp
11491118
);
11501119
target[k * 4 + 2] = BLEND(
1151-
src_alpha, target[k * 4 + 2], ink[2], tmp
1120+
src_alpha, target[k * 4 + 2], src_blue, tmp
11521121
);
11531122
target[k * 4 + 3] = CLIP8(
11541123
src_alpha +
@@ -1157,35 +1126,68 @@ font_render(FontObject *self, PyObject *args) {
11571126
)
11581127
);
11591128
} else {
1160-
target[k * 4 + 0] = ink[0];
1161-
target[k * 4 + 1] = ink[1];
1162-
target[k * 4 + 2] = ink[2];
1129+
/* paste unpremultiplied RGBA values */
1130+
target[k * 4 + 0] = src_red;
1131+
target[k * 4 + 1] = src_green;
1132+
target[k * 4 + 2] = src_blue;
11631133
target[k * 4 + 3] = src_alpha;
11641134
}
11651135
}
11661136
}
1167-
} else {
1168-
for (k = x0; k < x1; k++) {
1169-
unsigned int src_alpha = source[k] * convert_scale;
1170-
if (src_alpha > 0) {
1171-
target[k] =
1172-
target[k] > 0
1173-
? CLIP8(
1174-
src_alpha +
1175-
MULDIV255(
1176-
target[k], (255 - src_alpha), tmp
1137+
} else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
1138+
if (color) {
1139+
unsigned char *ink = (unsigned char *)&foreground_ink;
1140+
for (k = x0; k < x1; k++) {
1141+
unsigned int src_alpha = source[k] * convert_scale;
1142+
if (src_alpha > 0) {
1143+
if (target[k * 4 + 3] > 0) {
1144+
target[k * 4 + 0] = BLEND(
1145+
src_alpha, target[k * 4 + 0], ink[0], tmp
1146+
);
1147+
target[k * 4 + 1] = BLEND(
1148+
src_alpha, target[k * 4 + 1], ink[1], tmp
1149+
);
1150+
target[k * 4 + 2] = BLEND(
1151+
src_alpha, target[k * 4 + 2], ink[2], tmp
1152+
);
1153+
target[k * 4 + 3] = CLIP8(
1154+
src_alpha + MULDIV255(
1155+
target[k * 4 + 3],
1156+
(255 - src_alpha),
1157+
tmp
1158+
)
1159+
);
1160+
} else {
1161+
target[k * 4 + 0] = ink[0];
1162+
target[k * 4 + 1] = ink[1];
1163+
target[k * 4 + 2] = ink[2];
1164+
target[k * 4 + 3] = src_alpha;
1165+
}
1166+
}
1167+
}
1168+
} else {
1169+
for (k = x0; k < x1; k++) {
1170+
unsigned int src_alpha = source[k] * convert_scale;
1171+
if (src_alpha > 0) {
1172+
target[k] =
1173+
target[k] > 0
1174+
? CLIP8(
1175+
src_alpha +
1176+
MULDIV255(
1177+
target[k], (255 - src_alpha), tmp
1178+
)
11771179
)
1178-
)
1179-
: src_alpha;
1180+
: src_alpha;
1181+
}
11801182
}
11811183
}
1184+
} else {
1185+
PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode");
1186+
goto glyph_error;
11821187
}
1183-
} else {
1184-
PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode");
1185-
goto glyph_error;
11861188
}
1189+
source += bitmap.pitch;
11871190
}
1188-
source += bitmap.pitch;
11891191
}
11901192
x += glyph_info[i].x_advance;
11911193
y += glyph_info[i].y_advance;

0 commit comments

Comments
 (0)