|
From: <md...@us...> - 2007-08-07 15:29:29
|
Revision: 3680
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3680&view=rev
Author: mdboom
Date: 2007-08-07 08:29:25 -0700 (Tue, 07 Aug 2007)
Log Message:
-----------
Improve font rendering considerably using a vertical hinting hack.
Hinting is disabled for all of the "vector" backends, to correct
spacing problems.
Modified Paths:
--------------
trunk/matplotlib/lib/matplotlib/backends/backend_agg.py
trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py
trunk/matplotlib/lib/matplotlib/backends/backend_ps.py
trunk/matplotlib/lib/matplotlib/backends/backend_svg.py
trunk/matplotlib/lib/matplotlib/mathtext.py
trunk/matplotlib/src/ft2font.cpp
trunk/matplotlib/src/ft2font.h
Modified: trunk/matplotlib/lib/matplotlib/backends/backend_agg.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2007-08-07 14:34:24 UTC (rev 3679)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2007-08-07 15:29:25 UTC (rev 3680)
@@ -82,7 +82,7 @@
from matplotlib.cbook import enumerate, is_string_like, exception_to_str
from matplotlib.figure import Figure
from matplotlib.font_manager import fontManager
-from matplotlib.ft2font import FT2Font
+from matplotlib.ft2font import FT2Font, LOAD_DEFAULT
from matplotlib.mathtext import math_parse_s_ft2font
from matplotlib.transforms import lbwh_to_bbox
@@ -203,11 +203,10 @@
font = self._get_agg_font(prop)
if font is None: return None
- if len(s)==1 and ord(s)>127:
-
- font.load_char(ord(s))
+ if len(s) == 1 and ord(s) > 127:
+ font.load_char(ord(s), flags=LOAD_DEFAULT)
else:
- font.set_text(s, angle)
+ font.set_text(s, angle, flags=LOAD_DEFAULT)
font.draw_glyphs_to_bitmap()
#print x, y, int(x), int(y)
@@ -237,7 +236,7 @@
s, self.dpi.get(), prop)
return width, height
font = self._get_agg_font(prop)
- font.set_text(s, 0.0) # the width and height of unrotated string
+ font.set_text(s, 0.0, flags=LOAD_DEFAULT) # the width and height of unrotated string
w, h = font.get_width_height()
w /= 64.0 # convert from subpixels
h /= 64.0
Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-08-07 14:34:24 UTC (rev 3679)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-08-07 15:29:25 UTC (rev 3680)
@@ -28,7 +28,7 @@
from matplotlib.font_manager import fontManager
from matplotlib.afm import AFM
from matplotlib.dviread import Dvi
-from matplotlib.ft2font import FT2Font, FIXED_WIDTH, ITALIC, LOAD_NO_SCALE
+from matplotlib.ft2font import FT2Font, FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, LOAD_NO_HINTING
from matplotlib.mathtext import math_parse_s_pdf
from matplotlib.transforms import Bbox
from matplotlib import ttconv
@@ -517,7 +517,7 @@
def get_char_width(charcode):
unicode = decode_char(charcode)
- width = font.load_char(unicode, flags=LOAD_NO_SCALE).horiAdvance
+ width = font.load_char(unicode, flags=LOAD_NO_SCALE|LOAD_NO_HINTING).horiAdvance
return cvt(width)
firstchar, lastchar = 0, 255
@@ -1195,7 +1195,7 @@
else:
font = self._get_font_ttf(prop)
self.track_characters(font, s)
- font.set_text(s, 0.0)
+ font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
y += font.get_descent() / 64.0
self.file.output(Op.begin_text,
@@ -1222,7 +1222,7 @@
else:
font = self._get_font_ttf(prop)
- font.set_text(s, 0.0)
+ font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
w, h = font.get_width_height()
w /= 64.0
h /= 64.0
Modified: trunk/matplotlib/lib/matplotlib/backends/backend_ps.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2007-08-07 14:34:24 UTC (rev 3679)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2007-08-07 15:29:25 UTC (rev 3680)
@@ -19,7 +19,7 @@
from matplotlib.figure import Figure
from matplotlib.font_manager import fontManager
-from matplotlib.ft2font import FT2Font, KERNING_UNFITTED, KERNING_DEFAULT, KERNING_UNSCALED
+from matplotlib.ft2font import FT2Font, KERNING_DEFAULT, LOAD_NO_HINTING
from matplotlib.ttconv import convert_ttf_to_ps
from matplotlib.mathtext import math_parse_s_ps
from matplotlib.text import Text
@@ -292,7 +292,7 @@
return w, h
font = self._get_font_ttf(prop)
- font.set_text(s, 0.0)
+ font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
w, h = font.get_width_height()
w /= 64.0 # convert from subpixels
h /= 64.0
@@ -738,7 +738,7 @@
return self.draw_unicode(gc, x, y, s, prop, angle)
else:
font = self._get_font_ttf(prop)
- font.set_text(s,0)
+ font.set_text(s, 0, flags=LOAD_NO_HINTING)
self.track_characters(font, s)
self.set_color(*gc.get_rgb())
@@ -782,10 +782,10 @@
gind = 0
else:
name = font.get_glyph_name(gind)
- glyph = font.load_char(ccode)
+ glyph = font.load_char(ccode, flags=LOAD_NO_HINTING)
if lastgind is not None:
- kern = font.get_kerning(lastgind, gind, KERNING_UNFITTED)
+ kern = font.get_kerning(lastgind, gind, KERNING_DEFAULT)
else:
kern = 0
lastgind = gind
Modified: trunk/matplotlib/lib/matplotlib/backends/backend_svg.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2007-08-07 14:34:24 UTC (rev 3679)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2007-08-07 15:29:25 UTC (rev 3680)
@@ -8,7 +8,7 @@
from matplotlib.colors import rgb2hex
from matplotlib.figure import Figure
from matplotlib.font_manager import fontManager, FontProperties
-from matplotlib.ft2font import FT2Font, KERNING_UNFITTED, KERNING_DEFAULT, KERNING_UNSCALED
+from matplotlib.ft2font import FT2Font, KERNING_DEFAULT, LOAD_NO_HINTING
from matplotlib.mathtext import math_parse_s_ft2font_svg
from xml.sax.saxutils import escape as escape_xml_text
@@ -266,10 +266,10 @@
if gind is None:
ccode = ord('?')
gind = 0
- glyph = font.load_char(ccode)
+ glyph = font.load_char(ccode, flags=LOAD_NO_HINTING)
if lastgind is not None:
- kern = font.get_kerning(lastgind, gind, KERNING_UNFITTED)
+ kern = font.get_kerning(lastgind, gind, KERNING_DEFAULT)
else:
kern = 0
lastgind = gind
@@ -305,7 +305,7 @@
return char_id
path_data = []
- glyph = font.load_char(ord(char))
+ glyph = font.load_char(ord(char), flags=LOAD_NO_HINTING)
currx, curry = 0.0, 0.0
for step in glyph.path:
if step[0] == 0: # MOVE_TO
@@ -431,7 +431,7 @@
math_parse_s_ft2font_svg(s, 72, prop)
return width, height
font = self._get_font(prop)
- font.set_text(s, 0.0)
+ font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
w, h = font.get_width_height()
w /= 64.0 # convert from subpixels
h /= 64.0
Modified: trunk/matplotlib/lib/matplotlib/mathtext.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/mathtext.py 2007-08-07 14:34:24 UTC (rev 3679)
+++ trunk/matplotlib/lib/matplotlib/mathtext.py 2007-08-07 15:29:25 UTC (rev 3680)
@@ -148,7 +148,7 @@
from matplotlib.afm import AFM
from matplotlib.cbook import enumerate, iterable, Bunch, get_realpath_and_stat, \
is_string_like
-from matplotlib.ft2font import FT2Font, KERNING_UNFITTED
+from matplotlib.ft2font import FT2Font, KERNING_DEFAULT, LOAD_DEFAULT, LOAD_NO_HINTING
from matplotlib.font_manager import fontManager, FontProperties
from matplotlib._mathtext_data import latex_to_bakoma, \
latex_to_standard, tex2uni, type12uni, tex2type1, uni2type1
@@ -237,6 +237,9 @@
"""Return a backend specific tuple of things to return to the
backend after all processing is done."""
raise NotImplementedError()
+
+ def get_hinting_type(self):
+ return LOAD_NO_HINTING
class MathtextBackendAgg(MathtextBackend):
def set_canvas_size(self, w, h):
@@ -261,7 +264,10 @@
self.height,
self.fonts_object.get_fonts(),
self.fonts_object.get_used_characters())
-
+
+ def get_hinting_type(self):
+ return LOAD_DEFAULT
+
class MathtextBackendPs(MathtextBackend):
def __init__(self):
self.pswriter = StringIO()
@@ -487,7 +493,9 @@
font = cached_font.font
font.set_size(fontsize, dpi)
- glyph = font.load_char(num)
+ glyph = font.load_char(
+ num,
+ flags=self.mathtext_backend.get_hinting_type())
xmin, ymin, xmax, ymax = [val/64.0 for val in glyph.bbox]
offset = self._get_offset(cached_font, glyph, fontsize, dpi)
@@ -538,7 +546,7 @@
info1 = self._get_info(font1, sym1, fontsize1, dpi)
info2 = self._get_info(font2, sym2, fontsize2, dpi)
font = info1.font
- return font.get_kerning(info1.num, info2.num, KERNING_UNFITTED) / 64.0
+ return font.get_kerning(info1.num, info2.num, KERNING_DEFAULT) / 64.0
return 0.0
class BakomaFonts(TruetypeFonts):
Modified: trunk/matplotlib/src/ft2font.cpp
===================================================================
--- trunk/matplotlib/src/ft2font.cpp 2007-08-07 14:34:24 UTC (rev 3679)
+++ trunk/matplotlib/src/ft2font.cpp 2007-08-07 15:29:25 UTC (rev 3680)
@@ -5,6 +5,41 @@
#define FIXED_MAJOR(val) (*((short *) &val+1))
#define FIXED_MINOR(val) (*((short *) &val+0))
+/**
+ To improve the hinting of the fonts, this code uses a hack
+ presented here:
+
+ http://antigrain.com/research/font_rasterization/index.html
+
+ The idea is to limit the effect of hinting in the x-direction, while
+ preserving hinting in the y-direction. Since freetype does not
+ support this directly, the dpi in the x-direction is set higher than
+ in the y-direction, which affects the hinting grid. Then, a global
+ transform is placed on the font to shrink it back to the desired
+ size. While it is a bit surprising that the dpi setting affects
+ hinting, whereas the global transform does not, this is documented
+ behavior of freetype, and therefore hopefully unlikely to change.
+ The freetype 2 tutorial says:
+
+ NOTE: The transformation is applied to every glyph that is
+ loaded through FT_Load_Glyph and is completely independent of
+ any hinting process. This means that you won't get the same
+ results if you load a glyph at the size of 24 pixels, or a glyph
+ at the size at 12 pixels scaled by 2 through a transform,
+ because the hints will have been computed differently (except
+ you have disabled hints).
+
+ This hack is enabled only when VERTICAL_HINTING is defined, and will
+ only be effective when load_char and set_text are called with 'flags=
+ LOAD_DEFAULT', which is the default.
+ */
+#define VERTICAL_HINTING
+#ifdef VERTICAL_HINTING
+#define HORIZ_HINTING 8
+#else
+#define HORIZ_HINTING 1
+#endif
+
FT_Library _ft2Library;
FT2Image::FT2Image() : bRotated(false), buffer(NULL) {}
@@ -17,12 +52,12 @@
FT_BBox bbox;
FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_subpixels, &bbox );
- setattr("width", Py::Int( face->glyph->metrics.width) );
+ setattr("width", Py::Int( face->glyph->metrics.width / HORIZ_HINTING) );
setattr("height", Py::Int( face->glyph->metrics.height) );
- setattr("horiBearingX", Py::Int( face->glyph->metrics.horiBearingX) );
+ setattr("horiBearingX", Py::Int( face->glyph->metrics.horiBearingX / HORIZ_HINTING) );
setattr("horiBearingY", Py::Int( face->glyph->metrics.horiBearingY) );
- setattr("horiAdvance", Py::Int( face->glyph->metrics.horiAdvance) );
- setattr("linearHoriAdvance", Py::Int( face->glyph->linearHoriAdvance) );
+ setattr("horiAdvance", Py::Int( face->glyph->metrics.horiAdvance / HORIZ_HINTING) );
+ setattr("linearHoriAdvance", Py::Int( face->glyph->linearHoriAdvance / HORIZ_HINTING) );
setattr("vertBearingX", Py::Int( face->glyph->metrics.vertBearingX) );
setattr("vertBearingY", Py::Int( face->glyph->metrics.vertBearingY) );
@@ -341,7 +376,13 @@
}
// set a default fontsize 12 pt at 72dpi
+#ifdef VERTICAL_HINTING
+ error = FT_Set_Char_Size( face, 12 * 64, 0, 72 * HORIZ_HINTING, 72 );
+ static FT_Matrix transform = { 65536 / HORIZ_HINTING, 0, 0, 65536 };
+ FT_Set_Transform( face, &transform, 0 );
+#else
error = FT_Set_Char_Size( face, 12 * 64, 0, 72, 72 );
+#endif
//error = FT_Set_Char_Size( face, 20 * 64, 0, 80, 80 );
if (error) {
std::ostringstream s;
@@ -572,9 +613,17 @@
double ptsize = Py::Float(args[0]);
double dpi = Py::Float(args[1]);
+#ifdef VERTICAL_HINTING
int error = FT_Set_Char_Size( face, (long)(ptsize * 64), 0,
+ (unsigned int)dpi * HORIZ_HINTING,
+ (unsigned int)dpi );
+ static FT_Matrix transform = { 65536 / HORIZ_HINTING, 0, 0, 65536 };
+ FT_Set_Transform( face, &transform, 0 );
+#else
+ int error = FT_Set_Char_Size( face, (long)(ptsize * 64), 0,
(unsigned int)dpi,
(unsigned int)dpi );
+#endif
if (error)
throw Py::RuntimeError("Could not set the fontsize");
return Py::Object();
@@ -648,7 +697,7 @@
FT_Vector delta;
if (!FT_Get_Kerning( face, left, right, mode, &delta )) {
- return Py::Int(delta.x);
+ return Py::Int(delta.x / HORIZ_HINTING);
}
else {
return Py::Int(0);
@@ -665,7 +714,7 @@
"You must call this before draw_glyphs_to_bitmap\n"
"A sequence of x,y positions is returned";
Py::Object
-FT2Font::set_text(const Py::Tuple & args) {
+FT2Font::set_text(const Py::Tuple & args, const Py::Dict & kwargs) {
_VERBOSE("FT2Font::set_text");
args.verify_length(2);
@@ -687,6 +736,11 @@
angle = Py::Float(args[1]);
angle = angle/360.0*2*3.14159;
+
+ long flags = FT_LOAD_DEFAULT;
+ if (kwargs.hasKey("flags"))
+ flags = Py::Long(kwargs["flags"]);
+
//this computes width and height in subpixels so we have to divide by 64
matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
@@ -721,9 +775,9 @@
FT_Vector delta;
FT_Get_Kerning( face, previous, glyph_index,
FT_KERNING_DEFAULT, &delta );
- pen.x += delta.x;
+ pen.x += delta.x / HORIZ_HINTING;
}
- error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
+ error = FT_Load_Glyph( face, glyph_index, flags );
if ( error ) {
std::cerr << "\tcould not load glyph for " << thischar << std::endl;
continue;
@@ -794,7 +848,7 @@
}
char FT2Font::load_char__doc__[] =
-"load_char(charcode, flags=LOAD_DEFAULT)\n"
+"load_char(charcode, flags=LOAD_LOAD_DEFAULT)\n"
"\n"
"Load character with charcode in current fontfile and set glyph.\n"
"The flags argument can be a bitwise-or of the LOAD_XXX constants.\n"
@@ -881,22 +935,25 @@
FT_Int x,
FT_Int y) {
_VERBOSE("FT2Font::draw_bitmap");
- FT_Int i, j, p, q;
- FT_Int width = (FT_Int)image.width;
- FT_Int height = (FT_Int)image.height;
+ FT_Int image_width = (FT_Int)image.width;
+ FT_Int image_height = (FT_Int)image.height;
+ FT_Int char_width = bitmap->width;
+ FT_Int char_height = bitmap->rows;
- FT_Int x1 = CLAMP(x, 0, width);
- FT_Int y1 = CLAMP(y, 0, height);
- FT_Int x2 = CLAMP(x + bitmap->width, 0, width);
- FT_Int y2 = CLAMP(y + bitmap->rows, 0, height);
+ FT_Int x1 = CLAMP(x, 0, image_width);
+ FT_Int y1 = CLAMP(y, 0, image_height);
+ FT_Int x2 = CLAMP(x + char_width, 0, image_width);
+ FT_Int y2 = CLAMP(y + char_height, 0, image_height);
- for ( i = x1, p = MAX(0, -x); i < x2; ++i, ++p )
- {
- for ( j = y1, q = MAX(0, -y); j < y2; ++j, ++q )
- {
- image.buffer[i + j*width] |= bitmap->buffer[p + q*bitmap->pitch];
- }
- }
+ FT_Int x_start = MAX(0, -x);
+ FT_Int y_offset = y1 - MAX(0, -y);
+
+ for ( FT_Int i = y1; i < y2; ++i ) {
+ unsigned char* dst = image.buffer + (i * image_width + x1);
+ unsigned char* src = bitmap->buffer + (((i - y_offset) * bitmap->pitch) + x_start);
+ for ( FT_Int j = x1; j < x2; ++j, ++dst, ++src )
+ *dst |= *src;
+ }
}
char FT2Font::write_bitmap__doc__[] =
@@ -1038,15 +1095,14 @@
FT_BBox string_bbox = compute_string_bbox();
- image.width = (string_bbox.xMax-string_bbox.xMin) / 64+2;
- image.height = (string_bbox.yMax-string_bbox.yMin) / 64+2;
+ image.width = (string_bbox.xMax-string_bbox.xMin) / 64 + 2;
+ image.height = (string_bbox.yMax-string_bbox.yMin) / 64 + 2;
- image.offsetx = (int)(string_bbox.xMin/64.0);
+ image.offsetx = (int)(string_bbox.xMin / 64.0);
if (angle==0)
image.offsety = -image.height;
- else {
+ else
image.offsety = (int)(-string_bbox.yMax/64.0);
- }
size_t numBytes = image.width*image.height;
delete [] image.buffer;
@@ -1054,8 +1110,6 @@
for (size_t n=0; n<numBytes; n++)
image.buffer[n] = 0;
-
-
for ( size_t n = 0; n < glyphs.size(); n++ )
{
FT_BBox bbox;
@@ -1074,11 +1128,8 @@
// now, draw to our target surface (convert position)
//bitmap left and top in pixel, string bbox in subpixel
- FT_Int x = (FT_Int)(bitmap->left-string_bbox.xMin/64.);
- FT_Int y = (FT_Int)(string_bbox.yMax/64.-bitmap->top+1);
- //make sure the index is non-neg
- x = x<0?0:x;
- y = y<0?0:y;
+ FT_Int x = (FT_Int)(bitmap->left - (string_bbox.xMin / 64.));
+ FT_Int y = (FT_Int)((string_bbox.yMax / 64.) - bitmap->top + 1);
draw_bitmap( &bitmap->bitmap, x, y);
}
@@ -1173,12 +1224,7 @@
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[glyph->glyphInd];
- draw_bitmap( &bitmap->bitmap,
- //x + bitmap->left,
- x,
- //y+bitmap->top
- y
- );
+ draw_bitmap( &bitmap->bitmap, x, y);
return Py::Object();
}
@@ -1586,9 +1632,8 @@
behaviors().doc("Glyph");
behaviors().supportGetattr();
behaviors().supportSetattr();
+}
-
-}
void
FT2Font::init_type() {
_VERBOSE("FT2Font::init_type");
@@ -1603,7 +1648,7 @@
FT2Font::load_char__doc__);
add_varargs_method("draw_rect",&FT2Font::draw_rect,
FT2Font::draw_rect__doc__);
- add_varargs_method("draw_rect_filled",&FT2Font::draw_rect_filled,
+ add_varargs_method("draw_rect_filled",&FT2Font::draw_rect_filled,
FT2Font::draw_rect_filled__doc__);
add_varargs_method("draw_glyph_to_bitmap", &FT2Font::draw_glyph_to_bitmap,
FT2Font::draw_glyph_to_bitmap__doc__);
@@ -1620,7 +1665,7 @@
FT2Font::image_as_str__doc__);
add_keyword_method("load_char", &FT2Font::load_char,
FT2Font::load_char__doc__);
- add_varargs_method("set_text", &FT2Font::set_text,
+ add_keyword_method("set_text", &FT2Font::set_text,
FT2Font::set_text__doc__);
add_varargs_method("set_size", &FT2Font::set_size,
FT2Font::set_size__doc__);
@@ -1766,6 +1811,5 @@
}
ft2font_module::~ft2font_module() {
-
FT_Done_FreeType( _ft2Library );
}
Modified: trunk/matplotlib/src/ft2font.h
===================================================================
--- trunk/matplotlib/src/ft2font.h 2007-08-07 14:34:24 UTC (rev 3679)
+++ trunk/matplotlib/src/ft2font.h 2007-08-07 15:29:25 UTC (rev 3680)
@@ -56,7 +56,7 @@
Py::Object clear(const Py::Tuple & args);
Py::Object set_size(const Py::Tuple & args);
Py::Object set_charmap(const Py::Tuple & args);
- Py::Object set_text(const Py::Tuple & args);
+ Py::Object set_text(const Py::Tuple & args, const Py::Dict & kwargs);
Py::Object get_glyph(const Py::Tuple & args);
Py::Object get_kerning(const Py::Tuple & args);
Py::Object get_num_glyphs(const Py::Tuple & args);
@@ -91,9 +91,10 @@
std::vector<FT_Vector> pos;
std::vector<Glyph*> gms;
double angle;
+ double ptsize;
+ double dpi;
-
FT_BBox compute_string_bbox();
void draw_bitmap( FT_Bitmap* bitmap, FT_Int x, FT_Int y);
void set_scalable_attributes();
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|