Skip to content

Commit

Permalink
fix text clipping due to rounding
Browse files Browse the repository at this point in the history
  • Loading branch information
nulano committed Sep 1, 2020
1 parent bac9025 commit 35500aa
Showing 1 changed file with 75 additions and 125 deletions.
200 changes: 75 additions & 125 deletions src/_imagingft.c
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ font_getsize(FontObject* self, PyObject* args)
size_t i, count;
GlyphInfo *glyph_info = NULL;
PyObject *features = Py_None;
FT_Vector pen;

/* calculate size and bearing for a given string */

Expand Down Expand Up @@ -652,87 +653,54 @@ font_getsize(FontObject* self, PyObject* args)
FT_Glyph glyph;
face = self->face;
index = glyph_info[i].index;
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960
* Yifu Yu<[email protected]>, 2014-10-15
*/
load_flags = FT_LOAD_NO_BITMAP;
if (mask) {
load_flags |= FT_LOAD_TARGET_MONO;
}
error = FT_Load_Glyph(face, index, load_flags);
if (error) {
return geterror(error);
}

FT_Get_Glyph(face->glyph, &glyph);
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_SUBPIXELS, &bbox);
if (horizontal_dir) {
// 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;
}

pen.x = position + glyph_info[i].x_offset;
pen.y = glyph_info[i].y_offset;

position += glyph_info[i].x_advance;
advanced = position;

// adjust glyph end position if bearing extends past advanced point
offset = glyph_info[i].x_advance -
glyph_info[i].x_offset -
face->glyph->metrics.horiBearingX -
face->glyph->metrics.width;
if (offset < 0) {
advanced -= offset;
}
advanced = PIXEL(position);
if (advanced > x_max) {
x_max = advanced;
}

bbox.yMax += glyph_info[i].y_offset;
bbox.yMin += glyph_info[i].y_offset;
if (bbox.yMax > y_max) {
y_max = bbox.yMax;
}
if (bbox.yMin < y_min) {
y_min = bbox.yMin;
}
} else {
// 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);

// 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;
}
pen.x = glyph_info[i].x_offset;
pen.y = position + glyph_info[i].y_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;
}
advanced = PIXEL(position);
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_Set_Transform(face, NULL, &pen);

/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960
* Yifu Yu<[email protected]>, 2014-10-15
*/
load_flags = FT_LOAD_NO_BITMAP;
if (mask) {
load_flags |= FT_LOAD_TARGET_MONO;
}
error = FT_Load_Glyph(face, index, load_flags);
if (error)
return geterror(error);

error = FT_Get_Glyph(face->glyph, &glyph);
if (error)
return geterror(error);

FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox);

if (bbox.xMax > x_max)
x_max = bbox.xMax;
if (bbox.xMin < x_min)
x_min = bbox.xMin;
if (bbox.yMax > y_max)
y_max = bbox.yMax;
if (bbox.yMin < y_min)
y_min = bbox.yMin;

FT_Done_Glyph(glyph);
}
Expand All @@ -744,30 +712,31 @@ font_getsize(FontObject* self, PyObject* args)

x_anchor = y_anchor = 0;
if (face) {
FT_Set_Transform(face, NULL, NULL);
if (horizontal_dir) {
switch (anchor[0]) {
case 'l': // left
x_anchor = 0;
break;
case 'm': // middle (left + right) / 2
x_anchor = position / 2;
x_anchor = PIXEL(position / 2);
break;
case 'r': // right
x_anchor = position;
x_anchor = PIXEL(position);
break;
case 's': // vertical baseline
default:
goto bad_anchor;
}
switch (anchor[1]) {
case 'a': // ascender
y_anchor = self->face->size->metrics.ascender;
y_anchor = PIXEL(self->face->size->metrics.ascender);
break;
case 't': // top
y_anchor = y_max;
break;
case 'm': // middle (ascender + descender) / 2
y_anchor = (self->face->size->metrics.ascender + self->face->size->metrics.descender) / 2;
y_anchor = PIXEL((self->face->size->metrics.ascender + self->face->size->metrics.descender) / 2);
break;
case 's': // horizontal baseline
y_anchor = 0;
Expand All @@ -776,7 +745,7 @@ font_getsize(FontObject* self, PyObject* args)
y_anchor = y_min;
break;
case 'd': // descender
y_anchor = self->face->size->metrics.descender;
y_anchor = PIXEL(self->face->size->metrics.descender);
break;
default:
goto bad_anchor;
Expand All @@ -803,10 +772,10 @@ font_getsize(FontObject* self, PyObject* args)
y_anchor = 0;
break;
case 'm': // middle (top + bottom) / 2
y_anchor = position / 2;
y_anchor = PIXEL(position / 2);
break;
case 'b': // bottom
y_anchor = position;
y_anchor = PIXEL(position);
break;
case 'a': // ascender
case 's': // horizontal baseline
Expand All @@ -819,8 +788,8 @@ font_getsize(FontObject* self, PyObject* args)

return Py_BuildValue(
"(ii)(ii)",
PIXEL(x_max - x_min), PIXEL(y_max - y_min),
PIXEL(-x_anchor + x_min), PIXEL(y_anchor - y_max)
(x_max - x_min), (y_max - y_min),
(-x_anchor + x_min), -(-y_anchor + y_max)
);

bad_anchor:
Expand Down Expand Up @@ -855,6 +824,7 @@ font_render(FontObject* self, PyObject* args)
size_t i, count;
GlyphInfo *glyph_info;
PyObject *features = NULL;
FT_Vector pen;

if (!PyArg_ParseTuple(args, "On|izOzi:render", &string, &id, &mask, &dir, &features, &lang,
&stroke_width)) {
Expand Down Expand Up @@ -886,10 +856,15 @@ font_render(FontObject* self, PyObject* args)
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;
for (i = 0; i < count; i++) {
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);
if (error) {
return geterror(error);
Expand All @@ -900,40 +875,26 @@ font_render(FontObject* self, PyObject* args)

// compute baseline and adjust start position if glyph bearing extends before origin
if (horizontal_dir) {
temp = glyph_slot->metrics.horiBearingY + glyph_info[i].y_offset;
if (temp > baseline) {
baseline = temp;
}

temp = x + glyph_slot->metrics.horiBearingX + glyph_info[i].x_offset;
if (temp < offset) {
offset = temp;
}
if (glyph_slot->bitmap_top > baseline)
baseline = glyph_slot->bitmap_top;
if (glyph_slot->bitmap_left < offset)
offset = glyph_slot->bitmap_left;
x += glyph_info[i].x_advance;
} else {
// 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);

temp = -(glyph_slot->metrics.horiBearingX + glyph_info[i].x_offset);
if (temp > baseline) {
baseline = temp;
}

temp = y + glyph_slot->metrics.horiBearingY + glyph_info[i].y_offset;
if (temp > offset) {
offset = temp;
}
if (glyph_slot->bitmap_top > offset)
offset = glyph_slot->bitmap_top;
if (glyph_slot->bitmap_left < baseline)
baseline = glyph_slot->bitmap_left;
y += glyph_info[i].y_advance;
}
}

if (horizontal_dir) {
x = -offset;
x = (-offset + stroke_width) * 64;
y = (-baseline + (-stroke_width)) * 64;
} else {
y = -offset;
x = (-baseline + stroke_width) * 64;
y = (-offset + (-stroke_width)) * 64;
}

if (stroker == NULL) {
Expand All @@ -942,6 +903,11 @@ font_render(FontObject* self, PyObject* args)

for (i = 0; i < count; i++) {
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);
if (error) {
return geterror(error);
Expand All @@ -964,19 +930,12 @@ font_render(FontObject* self, PyObject* args)
bitmap_glyph = (FT_BitmapGlyph)glyph;

bitmap = bitmap_glyph->bitmap;
xx = bitmap_glyph->left;
yy = -bitmap_glyph->top;
} else {
bitmap = glyph_slot->bitmap;
}

// 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);
xx = glyph_slot->bitmap_left;
yy = -glyph_slot->bitmap_top;
}

x0 = 0;
Expand All @@ -989,17 +948,7 @@ font_render(FontObject* self, PyObject* args)
}

source = (unsigned char*) bitmap.buffer;
for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++) {
// 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;
}
for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) {
if (yy >= 0 && yy < im->ysize) {
// blend this glyph into the buffer
unsigned char *target = im->image8[yy] + xx;
Expand Down Expand Up @@ -1034,6 +983,7 @@ font_render(FontObject* self, PyObject* args)
}
}

FT_Set_Transform(self->face, NULL, NULL);
FT_Stroker_Done(stroker);
PyMem_Del(glyph_info);
Py_RETURN_NONE;
Expand Down

0 comments on commit 35500aa

Please sign in to comment.