Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c6d0183

Browse files
committed
Fix #253: Use advance width to calculate text bounding box
1 parent 41efb46 commit c6d0183

File tree

5 files changed

+61
-67
lines changed

5 files changed

+61
-67
lines changed

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2015-01-23 Text bounding boxes are now computed with advance width rather than
2+
ink area. This may result in slightly different placement of text.
3+
14
2014-10-27 Allowed selection of the backend using the `MPLBACKEND` environment
25
variable. Added documentation on backend selection methods.
36

lib/matplotlib/backends/backend_agg.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,15 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
202202
font.draw_glyphs_to_bitmap(antialiased=rcParams['text.antialiased'])
203203
d = font.get_descent() / 64.0
204204
# The descent needs to be adjusted for the angle
205+
xo, yo = font.get_bitmap_offset()
206+
xo /= 64.0
207+
yo /= 64.0
205208
xd = -d * np.sin(np.deg2rad(angle))
206209
yd = d * np.cos(np.deg2rad(angle))
207210

208211
#print x, y, int(x), int(y), s
209212
self._renderer.draw_text_image(
210-
font, np.round(x - xd), np.round(y + yd) + 1, angle, gc)
213+
font, np.round(x - xd + xo), np.round(y + yd + yo) + 1, angle, gc)
211214

212215
def get_text_width_height_descent(self, s, prop, ismath):
213216
"""

src/ft2font.cpp

Lines changed: 36 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -569,46 +569,6 @@ void FT2Font::select_charmap(unsigned long i)
569569
}
570570
}
571571

572-
FT_BBox FT2Font::compute_string_bbox()
573-
{
574-
FT_BBox bbox;
575-
/* initialize string bbox to "empty" values */
576-
bbox.xMin = bbox.yMin = 32000;
577-
bbox.xMax = bbox.yMax = -32000;
578-
579-
int right_side = 0;
580-
for (size_t n = 0; n < glyphs.size(); n++) {
581-
FT_BBox glyph_bbox;
582-
FT_Glyph_Get_CBox(glyphs[n], ft_glyph_bbox_subpixels, &glyph_bbox);
583-
if (glyph_bbox.xMin < bbox.xMin) {
584-
bbox.xMin = glyph_bbox.xMin;
585-
}
586-
if (glyph_bbox.yMin < bbox.yMin) {
587-
bbox.yMin = glyph_bbox.yMin;
588-
}
589-
if (glyph_bbox.xMin == glyph_bbox.xMax) {
590-
right_side += glyphs[n]->advance.x >> 10;
591-
if (right_side > bbox.xMax) {
592-
bbox.xMax = right_side;
593-
}
594-
} else {
595-
if (glyph_bbox.xMax > bbox.xMax) {
596-
bbox.xMax = glyph_bbox.xMax;
597-
}
598-
}
599-
if (glyph_bbox.yMax > bbox.yMax)
600-
bbox.yMax = glyph_bbox.yMax;
601-
}
602-
/* check that we really grew the string bbox */
603-
if (bbox.xMin > bbox.xMax) {
604-
bbox.xMin = 0;
605-
bbox.yMin = 0;
606-
bbox.xMax = 0;
607-
bbox.yMax = 0;
608-
}
609-
return bbox;
610-
}
611-
612572
int FT2Font::get_kerning(int left, int right, int mode)
613573
{
614574
if (!FT_HAS_KERNING(face)) {
@@ -617,7 +577,7 @@ int FT2Font::get_kerning(int left, int right, int mode)
617577
FT_Vector delta;
618578

619579
if (!FT_Get_Kerning(face, left, right, mode, &delta)) {
620-
return (int)(delta.x / hinting_factor);
580+
return (int)(delta.x) / (hinting_factor << 6);
621581
} else {
622582
return 0;
623583
}
@@ -641,17 +601,22 @@ void FT2Font::set_text(
641601
pen.x = 0;
642602
pen.y = 0;
643603

604+
bbox.xMin = bbox.yMin = 32000;
605+
bbox.xMax = bbox.yMax = -32000;
606+
644607
for (unsigned int n = 0; n < N; n++) {
645608
std::string thischar("?");
646609
FT_UInt glyph_index;
610+
FT_BBox glyph_bbox;
611+
FT_Pos last_advance;
647612

648613
glyph_index = FT_Get_Char_Index(face, codepoints[n]);
649614

650615
// retrieve kerning distance and move pen position
651616
if (use_kerning && previous && glyph_index) {
652617
FT_Vector delta;
653618
FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
654-
pen.x += delta.x / hinting_factor;
619+
pen.x += (delta.x << 10) / (hinting_factor << 16);
655620
}
656621
error = FT_Load_Glyph(face, glyph_index, flags);
657622
if (error) {
@@ -669,18 +634,30 @@ void FT2Font::set_text(
669634
}
670635
// ignore errors, jump to next glyph
671636

637+
last_advance = face->glyph->advance.x;
672638
FT_Glyph_Transform(thisGlyph, 0, &pen);
639+
FT_Glyph_Transform(thisGlyph, &matrix, 0);
673640
xys.push_back(pen.x);
674641
xys.push_back(pen.y);
675-
pen.x += face->glyph->advance.x;
642+
643+
FT_Glyph_Get_CBox(thisGlyph, ft_glyph_bbox_subpixels, &glyph_bbox);
644+
645+
bbox.xMin = std::min(bbox.xMin, glyph_bbox.xMin);
646+
bbox.xMax = std::max(bbox.xMax, glyph_bbox.xMax);
647+
bbox.yMin = std::min(bbox.yMin, glyph_bbox.yMin);
648+
bbox.yMax = std::max(bbox.yMax, glyph_bbox.yMax);
649+
650+
pen.x += last_advance;
676651

677652
previous = glyph_index;
678653
glyphs.push_back(thisGlyph);
679654
}
680655

681-
// now apply the rotation
682-
for (unsigned int n = 0; n < glyphs.size(); n++) {
683-
FT_Glyph_Transform(glyphs[n], &matrix, 0);
656+
FT_Vector_Transform(&pen, &matrix);
657+
advance = pen.x;
658+
659+
if (bbox.xMin > bbox.xMax) {
660+
bbox.xMin = bbox.yMin = bbox.xMax = bbox.yMax = 0;
684661
}
685662
}
686663

@@ -722,30 +699,29 @@ void FT2Font::load_glyph(FT_UInt glyph_index, FT_UInt32 flags)
722699

723700
void FT2Font::get_width_height(long *width, long *height)
724701
{
725-
FT_BBox bbox = compute_string_bbox();
726-
727-
*width = bbox.xMax - bbox.xMin;
702+
*width = advance;
728703
*height = bbox.yMax - bbox.yMin;
729704
}
730705

731706
long FT2Font::get_descent()
732707
{
733-
FT_BBox bbox = compute_string_bbox();
734708
return -bbox.yMin;
735709
}
736710

711+
void FT2Font::get_bitmap_offset(long *x, long *y)
712+
{
713+
*x = bbox.xMin;
714+
*y = 0;
715+
}
716+
737717
void FT2Font::draw_glyphs_to_bitmap(bool antialiased)
738718
{
739-
FT_BBox string_bbox = compute_string_bbox();
740-
size_t width = (string_bbox.xMax - string_bbox.xMin) / 64 + 2;
741-
size_t height = (string_bbox.yMax - string_bbox.yMin) / 64 + 2;
719+
size_t width = (bbox.xMax - bbox.xMin) / 64 + 2;
720+
size_t height = (bbox.yMax - bbox.yMin) / 64 + 2;
742721

743722
image.resize(width, height);
744723

745724
for (size_t n = 0; n < glyphs.size(); n++) {
746-
FT_BBox bbox;
747-
FT_Glyph_Get_CBox(glyphs[n], ft_glyph_bbox_pixels, &bbox);
748-
749725
error = FT_Glyph_To_Bitmap(
750726
&glyphs[n], antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1);
751727
if (error) {
@@ -756,22 +732,17 @@ void FT2Font::draw_glyphs_to_bitmap(bool antialiased)
756732
// now, draw to our target surface (convert position)
757733

758734
// bitmap left and top in pixel, string bbox in subpixel
759-
FT_Int x = (FT_Int)(bitmap->left - (string_bbox.xMin / 64.));
760-
FT_Int y = (FT_Int)((string_bbox.yMax / 64.) - bitmap->top + 1);
735+
FT_Int x = (FT_Int)(bitmap->left - (bbox.xMin / 64.));
736+
FT_Int y = (FT_Int)((bbox.yMax / 64.) - bitmap->top + 1);
761737

762738
image.draw_bitmap(&bitmap->bitmap, x, y);
763739
}
764740
}
765741

766742
void FT2Font::get_xys(bool antialiased, std::vector<double> &xys)
767743
{
768-
FT_BBox string_bbox = compute_string_bbox();
769-
770744
for (size_t n = 0; n < glyphs.size(); n++) {
771745

772-
FT_BBox bbox;
773-
FT_Glyph_Get_CBox(glyphs[n], ft_glyph_bbox_pixels, &bbox);
774-
775746
error = FT_Glyph_To_Bitmap(
776747
&glyphs[n], antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1);
777748
if (error) {
@@ -781,8 +752,8 @@ void FT2Font::get_xys(bool antialiased, std::vector<double> &xys)
781752
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n];
782753

783754
// bitmap left and top in pixel, string bbox in subpixel
784-
FT_Int x = (FT_Int)(bitmap->left - string_bbox.xMin / 64.);
785-
FT_Int y = (FT_Int)(string_bbox.yMax / 64. - bitmap->top + 1);
755+
FT_Int x = (FT_Int)(bitmap->left - bbox.xMin / 64.);
756+
FT_Int y = (FT_Int)(bbox.yMax / 64. - bitmap->top + 1);
786757
// make sure the index is non-neg
787758
x = x < 0 ? 0 : x;
788759
y = y < 0 ? 0 : y;

src/ft2font.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class FT2Font
7777
void load_char(long charcode, FT_UInt32 flags);
7878
void load_glyph(FT_UInt glyph_index, FT_UInt32 flags);
7979
void get_width_height(long *width, long *height);
80+
void get_bitmap_offset(long *x, long *y);
8081
long get_descent();
8182
// TODO: Since we know the size of the array upfront, we probably don't
8283
// need to dynamically allocate like this
@@ -121,12 +122,13 @@ class FT2Font
121122
FT_Error error;
122123
std::vector<FT_Glyph> glyphs;
123124
std::vector<FT_Vector> pos;
125+
FT_BBox bbox;
126+
FT_Pos advance;
124127
double angle;
125128
double ptsize;
126129
double dpi;
127130
long hinting_factor;
128131

129-
FT_BBox compute_string_bbox();
130132
void set_scalable_attributes();
131133

132134
// prevent copying

src/ft2font_wrapper.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,20 @@ static PyObject *PyFT2Font_get_width_height(PyFT2Font *self, PyObject *args, PyO
765765
return Py_BuildValue("ll", width, height);
766766
}
767767

768+
const char *PyFT2Font_get_bitmap_offset__doc__ =
769+
"w, h = get_bitmap_offset()\n"
770+
"\n"
771+
"Get the offset in 26.6 subpixels for the bitmap if ink hangs left or below (0, 0)\n";
772+
773+
static PyObject *PyFT2Font_get_bitmap_offset(PyFT2Font *self, PyObject *args, PyObject *kwds)
774+
{
775+
long x, y;
776+
777+
CALL_CPP("get_bitmap_offset", (self->x->get_bitmap_offset(&x, &y)));
778+
779+
return Py_BuildValue("ll", x, y);
780+
}
781+
768782
const char *PyFT2Font_get_descent__doc__ =
769783
"d = get_descent()\n"
770784
"\n"
@@ -1572,6 +1586,7 @@ static PyTypeObject *PyFT2Font_init_type(PyObject *m, PyTypeObject *type)
15721586
{"load_char", (PyCFunction)PyFT2Font_load_char, METH_VARARGS|METH_KEYWORDS, PyFT2Font_load_char__doc__},
15731587
{"load_glyph", (PyCFunction)PyFT2Font_load_glyph, METH_VARARGS|METH_KEYWORDS, PyFT2Font_load_glyph__doc__},
15741588
{"get_width_height", (PyCFunction)PyFT2Font_get_width_height, METH_NOARGS, PyFT2Font_get_width_height__doc__},
1589+
{"get_bitmap_offset", (PyCFunction)PyFT2Font_get_bitmap_offset, METH_NOARGS, PyFT2Font_get_bitmap_offset__doc__},
15751590
{"get_descent", (PyCFunction)PyFT2Font_get_descent, METH_NOARGS, PyFT2Font_get_descent__doc__},
15761591
{"draw_glyphs_to_bitmap", (PyCFunction)PyFT2Font_draw_glyphs_to_bitmap, METH_VARARGS|METH_KEYWORDS, PyFT2Font_draw_glyphs_to_bitmap__doc__},
15771592
{"get_xys", (PyCFunction)PyFT2Font_get_xys, METH_VARARGS|METH_KEYWORDS, PyFT2Font_get_xys__doc__},

0 commit comments

Comments
 (0)