After trying many different things to fix these problems I finally
found a solution that works at any angle, any font and any size, at
least for the many tests I have done.
So here it is:
// let x and y be the anchor point where you would like to print your
text
// and angle the printing angle in degrees
if ( Raphael.vml ) {
// apply an offset to and y only if VML is in use
x -= 2 * Math.sin ( radians ) // be careful, use 'minus equal'
assignment here
y += 2 * Math.cos( radians )
}
// Now print, anchor, and rotate your text as usual
paper.text( x, y, 'Lorem Ipsum' ).attr({"text-anchor":anchor, "font-
size":size, "font-family":font}).rotate( x, y )
// Note: Always apply rotation after anchoring or you might have some
surprises in some browsers
To see this in action for a variety of angles, anchors, and to verify
that it works for the font of your liking and size, try this in the
Raphael Playground:
size=20 // your font size
anchor='start' // desired anchor
font='Arial'
offset = 2 // if you set this to zero, you will see the behavior
without any adjustment
// You should not need to edit parameters beyond this line
cx = 320, cy = 240, r = 60
for ( angle=0; angle < 360; angle += 30 ) {
radians = angle * Math.PI / 180
sin = Math.sin( radians ), cos = Math.cos( radians )
xo = - offset * sin
yo = + offset * cos
x = cx + r * cos, y = cy + r * sin
t = paper.text( x + xo, y + yo, "eeeeee" ).attr( {"text-
anchor":anchor, "font-size":size, "font-family":font} ).rotate(angle,
x + xo, y + yo);
paper.path(['M',x-20,y,'h',40,'M',x,y-20,'v',40].join(' ')).attr
({stroke:'red'})
}
There is still a slight difference at angles +/- 150 degrees for very
small fonts but this should be good enough most applications.
If you tune the offset above, you will see the discrepancies if offset
== 0 (no offset applied), at offset == 1 this is better but there is
still some mis-alignments, at offset == 2 this is almost perfect, and
at offset == 3 this is too much.
I am implementing this in this graphic library: http://github.com/uiteoi/ico
You can also see the result in action, on angled labels on my
development test suite:
http://ser2.dialink.com:3001/
Hope this helps.
size=12, anchor='start', font='Arial'
offset = 2
cx = 320, cy = 240, r = 60
for ( angle=0; angle < 360; angle += 30 ) {
radians = angle * Math.PI / 180
sin = Math.sin( radians ), cos = Math.cos( radians )
yo = + offset * cos
xo = - offset * sin
x = cx + r * cos, y = cy + r * sin
t = paper.text( x + xo, y + yo, "eeeeee" ).attr( {"text-
anchor":anchor, "font-size":size, "font-family":font} ).rotate(angle,
x + xo, y + yo);
paper.path(['M', x -20 * cos, y - 20 * sin, 'l', +40 * cos, 40 *
sin].join(' ')).attr({stroke:'red'})
// paper.path(['M', x +20 * sin, y - 20 * cos, 'l', -40 * sin, 40 *
cos].join(' ')).attr({stroke:'red'})
paper.circle(cx,cy,r).attr({stroke:'red'})
}
With this demo one can see that the optimal offset at angles +/- 150
degrees is 3. So if you use these unusual angles you could improve
further the adjustments.
The font-size offset is necessary to show the same alignment as in SVG
browsers. The text is not drawn on the base line in any browser but
slightly above the mid-line of lower case letters.
With this method it is also be possible to re-align all browsers on
the text baseline, which would correspond to the default alignment in
the SVG specification:
http://www.w3.org/TR/SVG/text.html#AlignmentBaselineProperty
To do this substract font-size / 3 to all browsers.
You can check commented source code of this recipe on github here:
http://gist.github.com/265480
I would like Dimitry's opinion on this matter.