You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-dev@xmlgraphics.apache.org by Daniel Phillips <da...@undetected.net> on 2003/07/03 19:57:51 UTC
[patch] kerning attribute fix
In order to precisely position text characters in SVG, it's often necessary
to set kerning='0' on text elements to ensure that unwanted tweaks aren't
made to character positions during display or printing. However, Batik
handles the 'kerning' attribute incorrectly. It treats a kerning value as
an absolute glyph-spacing value rather than as an *adjustment* to glyph
spacing.
For example, Batik would render this text element: <text
kerning='0'>hello</text>, with zero spacing between each glyph. Each glyph
would be touching. That would be an incorrect result. What should happen
instead is that each glyph should be layed-out with some whitespace (namely
the glyph's left and right-side bearing) surrounding it. The value 0 here
simply means that there should be zero kerning adjustment, not zero spacing
between characters.
(For reference, here are some nice diagrams showing font terminology[1] and
kerning[2].)
This example[3] shows the problem in Batik. The example is from the SVG spec
but with a kerning value of 0 on the text element for illustration.
So, how do we get the correct result? The SVG1.1 spec says this: "If a
<length> is provided, then auto-kerning is disabled. Instead,
inter-character spacing is set to the given <length>."[4] The term
"inter-character spacing" here is vague -- what exactly do they mean by a
character? How wide is a character? Batik currently interprets a character
width as the bounding-box width of the glyph. Given this interpretation,
Batik is operating correctly. But a better interpretation for width, I
think, is the bounding-box width plus the left and right-side bearing of the
glyph -- i.e the full advance width of the glyph.
The spec supports this interpretation: It goes on to say, "The most common
scenario, other than auto, is to set 'kerning' to a value of 0 so that
auto-kerning is disabled." The intent here is that you set 'kerning' to 0 to
disable kerning. That implies that the kerning value is an adjustment rather
than an absolute value. Remember that kerning is a subtle adjustment to
glyph spacing to make text look nicer. Kerned and un-kerned text should look
much the same in gross terms. Un-kerned text certainly shouldn't look like
it's had all of its spacing removed. (Adobe's viewer implementation supports
these assertions).
The method responsible for applying the 'kerning' value is
org.apache.batik.gvt.text.GlyphLayout.doSpacing. I've reworked it and
provided a patch[5]. An additional problem with the original code is that it
attempts to remove any auto-kerning values from the GlyphVector before
applying the explicit 'kerning' value. In doing so, it also loses explicit
x-coords (e.g. in <text x="10,20,30">). However, since neither Java nor
Batik currently actually do any kerning, it made sense to remove this code.
Dan Phillips
Software Mechanics
[1]
http://developer.apple.com/documentation/Carbon/Conceptual/ATSUI_Concepts/atsui_chap2/chapter_2_section_6.html
[2]
http://developer.apple.com/documentation/Carbon/Conceptual/ATSUI_Concepts/atsui_chap2/chapter_2_section_8.html#//apple_ref/doc/uid/TP30000028/TPXREF149
[3]
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="10cm" height="3cm" viewBox="0 0 1000 300"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<desc>Example text01 - 'Hello, out there' in blue</desc>
<text x="250" y="150" kerning="0"
font-family="Verdana" font-size="55" fill="blue" >
Hello, out there
</text>
<!-- Show outline of canvas using 'rect' element -->
<rect x="1" y="1" width="998" height="298"
fill="none" stroke="blue" stroke-width="2" />
</svg>
[4]
http://www.w3.org/TR/SVG11/text.html#KerningProperty
[5]
Index: GlyphLayout.java
===================================================================
RCS file: /home/cvspublic/xml-batik/sources/org/apache/batik/gvt/text/GlyphLayout.java,v
retrieving revision 1.52
diff -u -r1.52 GlyphLayout.java
--- GlyphLayout.java 8 May 2003 11:39:13 -0000 1.52
+++ GlyphLayout.java 1 Jul 2003 18:02:13 -0000
@@ -1391,7 +1391,7 @@
/**
* Performs any spacing adjustments required and returns the new advance
- * value.
+ * value. Assumes that the text is un-kerned.
*
* @param kern The kerning adjustment to apply to the space
* between each char.
@@ -1400,21 +1400,18 @@
protected Point2D doSpacing(Float kern,
Float letterSpacing,
Float wordSpacing) {
- boolean autoKern = true;
boolean doWordSpacing = false;
- boolean doLetterSpacing = false;
float kernVal = 0f;
float letterSpacingVal = 0f;
float wordSpacingVal = 0f;
if ((kern instanceof Float) && (!kern.isNaN())) {
kernVal = kern.floatValue();
- autoKern = false;
//System.out.println("KERNING: "+kernVal);
}
+
if ((letterSpacing instanceof Float) && (!letterSpacing.isNaN())) {
letterSpacingVal = letterSpacing.floatValue();
- doLetterSpacing = true;
//System.out.println("LETTER-SPACING: "+letterSpacingVal);
}
if ((wordSpacing instanceof Float) && (!wordSpacing.isNaN())) {
@@ -1437,28 +1434,19 @@
advance.getY() - (gv.getGlyphPosition(numGlyphs-1).getY() - y));
try {
- // do letter spacing first
- if ((numGlyphs > 1) && (doLetterSpacing || !autoKern)) {
+ if (numGlyphs > 1) {
for (int i=1; i<=numGlyphs; ++i) {
Point2D gpos = gv.getGlyphPosition(i);
dx = (float)gpos.getX()-(float)prevPos.getX();
dy = (float)gpos.getY()-(float)prevPos.getY();
- if (autoKern) {
- if (vertical) dy += letterSpacingVal;
- else dx += letterSpacingVal;
+
+ // apply explicit kerning and letter-spacing adjustments
+ if (vertical) {
+ dy += kernVal + letterSpacingVal;
} else {
- // apply explicit kerning adjustments,
- // discarding any auto-kern dx values
- if (vertical) {
- dy = (float)
- gv.getGlyphMetrics(i-1).getBounds2D().getHeight()+
- kernVal + letterSpacingVal;
- } else {
- dx = (float)
- gv.getGlyphMetrics(i-1).getBounds2D().getWidth()+
- kernVal + letterSpacingVal;
- }
+ dx += kernVal + letterSpacingVal;
}
+
x += dx;
y += dy;
newPositions[i] = new Point2D.Float(x, y);
@@ -1472,25 +1460,13 @@
}
}
- // adjust the advance of the last character
- if (autoKern) {
- if (vertical) {
- lastCharAdvance.setLocation(lastCharAdvance.getX(),
- lastCharAdvance.getY() + letterSpacingVal);
- } else {
- lastCharAdvance.setLocation(lastCharAdvance.getX()
- + letterSpacingVal, lastCharAdvance.getY());
- }
+ // adjust the advance of the last character
+ if (vertical) {
+ lastCharAdvance.setLocation(lastCharAdvance.getX(),
+ lastCharAdvance.getY() + kernVal + letterSpacingVal);
} else {
- if (vertical) {
- lastCharAdvance.setLocation(lastCharAdvance.getX(),
- gv.getGlyphMetrics(numGlyphs-2).getBounds2D().getHeight()+
- kernVal + letterSpacingVal);
- } else {
- lastCharAdvance.setLocation(
- gv.getGlyphMetrics(numGlyphs-2).getBounds2D().getWidth()+
- kernVal + letterSpacingVal, lastCharAdvance.getY());
- }
+ lastCharAdvance.setLocation(lastCharAdvance.getX() + kernVal + letterSpacingVal,
+ lastCharAdvance.getY());
}
---------------------------------------------------------------------
To unsubscribe, e-mail: batik-dev-unsubscribe@xml.apache.org
For additional commands, e-mail: batik-dev-help@xml.apache.org
Re: [patch] kerning attribute fix
Posted by Thomas DeWeese <Th...@Kodak.com>.
Daniel Phillips wrote:
>In order to precisely position text characters in SVG, it's often necessary
>to set kerning='0' on text elements to ensure that unwanted tweaks aren't
>made to character positions during display or printing. However, Batik
>handles the 'kerning' attribute incorrectly. It treats a kerning value as
>an absolute glyph-spacing value rather than as an *adjustment* to glyph
>spacing.
>
>
[...]
>The spec supports this interpretation: It goes on to say, "The most common
>scenario, other than auto, is to set 'kerning' to a value of 0 so that
>auto-kerning is disabled." The intent here is that you set 'kerning' to 0 to
>disable kerning. That implies that the kerning value is an adjustment rather
>than an absolute value. Remember that kerning is a subtle adjustment to
>glyph spacing to make text look nicer. Kerned and un-kerned text should look
>much the same in gross terms. Un-kerned text certainly shouldn't look like
>it's had all of its spacing removed. (Adobe's viewer implementation supports
>these assertions).
>
>
Agree with this interpretation of the specification as it is clearly
in line with standard industry practice
(although the specification is a little unclear)
>The method responsible for applying the 'kerning' value is
>org.apache.batik.gvt.text.GlyphLayout.doSpacing. I've reworked it and
>provided a patch[5]. An additional problem with the original code is that it
>attempts to remove any auto-kerning values from the GlyphVector before
>applying the explicit 'kerning' value. In doing so, it also loses explicit
>x-coords (e.g. in <text x="10,20,30">). However, since neither Java nor
>Batik currently actually do any kerning, it made sense to remove this code.
>
>
We do currently do kerning from font elements. So I rewrote your
patch to properly remove font
kerning and use the kern value when present.
---------------------------------------------------------------------
To unsubscribe, e-mail: batik-dev-unsubscribe@xml.apache.org
For additional commands, e-mail: batik-dev-help@xml.apache.org