1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
Description:
Problem
=======
On OpenJDK JRE and using some TrueType font, JasperReports does not
display text element which height is just the same as the font size
eg. { height="8", size="8", font="IPA mincho" }.
JasperReports checks the text size before drawing the text elements.
If (ascent + descent) of text is greater than the height of text
element, this text is not drawn.
In above case, Sun Java returns the same height (ascent + descent =
fontsize), but OpenJDK returns the text height greater than font size,
so not drawn.
Sample code (includes Japanese char)
===================================
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
public class JavaApplication1 {
public static void main(String[] args) throws Exception {
Font f = new Font("IPA明朝",Font.PLAIN, 8);
TextLayout layout = new TextLayout("IPA明朝", f,
new FontRenderContext(null, true, true));
System.out.println("Ascent: " + layout.getAscent());
System.out.println("Descent: " + layout.getDescent());
}
}
Result of this code
===================
Sun Java 6 (sun-java6-jre 6.26-0squeeze1)
> Ascent: 7.0390625
> Descent: 0.9609375
OpenJDK 6 (openjdk-6-jre 6b24~pre2-1)
> Ascent: 7.046875
> Descent: 0.96875
OpenJDK 7 (openjdk-7-jre 7~b147-2.0-1)
> Ascent: 7.046875
> Descent: 0.96875
Sun Java returns correct height, but OpenJDK returns greater value
than Sun's.
Analysis of Source code
=======================
In OpenJDK6/7 native method sun.font.FreetypeFontScaler.getFontMetricsNative():
jdk/src/share/native/sun/font/freetypeScaler.c
>JNIEXPORT jobject JNICALL
>Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
> JNIEnv *env, jobject scaler, jobject font2D,
> jlong pScalerContext, jlong pScaler) {
(snip)
> /* ascent */
> ax = 0;
> ay = -(jfloat) FT26Dot6ToFloat(FT_MulFix(
> ((jlong) scalerInfo->face->ascender + bmodifier/2),
> (jlong) scalerInfo->face->size->metrics.y_scale));
> /* descent */
> dx = 0;
> dy = -(jfloat) FT26Dot6ToFloat(FT_MulFix(
> ((jlong) scalerInfo->face->descender + bmodifier/2),
> (jlong) scalerInfo->face->size->metrics.y_scale));
(snip)
> metrics = (*env)->NewObject(env,
> sunFontIDs.strikeMetricsClass,
> sunFontIDs.strikeMetricsCtr,
> ax, ay, dx, dy, bx, by, lx, ly, mx, my);
>
> return metrics;
>}
This code uses FT_MulFix to convert size. But FT_MulFix sometimes rounds
up and loses precision.
In FreeType2's FT_MulFix:
freetype-2.4.8/src/base/ftcalc.c
> c = (FT_Long)( ( (FT_Int64)a * b + 0x8000L ) >> 16 );
If both ascent and descent are rounded up, (ascent + descent) is greater
than original height.
In the sample case (IPA mincho font),
bmodifier = 0
scalerInfo->face->ascender = 1802L
scalerInfo->face->descender = -246L
scalerInfo->face->size->metrics.y_scale = 16384L
In this case, 1802 mod 4 = 2 and 246 mod 4 = 2, so both are rounded up.
This causes (ay + dy) > font-size.
(Note: (Sun) 1802.0/256.0 = 7.0390625, (OpenJDK) 1804.0/256.0 = 7.046875)
Suggested fix
=============
Fix to keep the precision in the font metrics conversion in
Java_sun_font_FreetypeFontScaler_getFontMetricsNative().
Author: Nobuhiro Ban <ban.nobuhiro@gmail.com>
Bug-Debian: http://bugs.debian.org/657854
--- openjdk/jdk/src/share/native/sun/font/freetypeScaler.c
+++ openjdk/jdk/src/share/native/sun/font/freetypeScaler.c
@@ -488,14 +488,15 @@
/**** Note: only some metrics are affected by styling ***/
+#define FT_MulFixFloatShift6(a, b) (((float) (a)) * ((float) (b)) / 65536.0 / 64.0)
/* ascent */
ax = 0;
- ay = -(jfloat) FT26Dot6ToFloat(FT_MulFix(
+ ay = -(jfloat) (FT_MulFixFloatShift6(
((jlong) scalerInfo->face->ascender + bmodifier/2),
(jlong) scalerInfo->face->size->metrics.y_scale));
/* descent */
dx = 0;
- dy = -(jfloat) FT26Dot6ToFloat(FT_MulFix(
+ dy = -(jfloat) (FT_MulFixFloatShift6(
((jlong) scalerInfo->face->descender + bmodifier/2),
(jlong) scalerInfo->face->size->metrics.y_scale));
/* baseline */
@@ -503,7 +504,7 @@
/* leading */
lx = 0;
- ly = (jfloat) FT26Dot6ToFloat(FT_MulFix(
+ ly = (jfloat) (FT_MulFixFloatShift6(
(jlong) scalerInfo->face->height + bmodifier,
(jlong) scalerInfo->face->size->metrics.y_scale))
+ ay - dy;
|