Report item presentation
BIRT report engine provides the org.eclipse.birt.report.engine
.reportitemPresentation extension point, as shown in Figure 23‑23. This extension point defines the presentation of extended report items.
Figure 23‑23 Defining the presentation
The extension property RotatedText binds the engine extension to the model extension. The property name must be set to RotatedText to match the name of the model’s RotatedText property.
The presentation implementer class is set to org.eclipse.birt.sample
.reportitem.rotatedtext.RotatedTextPresentationImpl and must implement the IReportItemPresentation interface. The implementation of the class creates the text image dynamically, and uses the Swing graphics API to render the rotated text. Listing 23‑12 shows the code for this class.
Listing 23‑12 RotatedTextPresentationImpl class
public class RotatedTextPresentationImpl
extends ReportItemPresentationBase
{
private RotatedTextItem textItem;
public void setModelObject( ExtendedItemHandle modelHandle )
{
try {
textItem = (RotatedTextItem) modelHandle.getReportItem();
}
catch ( ExtendedElementException e )
{
e.printStackTrace( );
}
}
 
public int getOutputType( )
{
return OUTPUT_AS_IMAGE;
}
 
public Object onRowSets( IRowSet[] rowSets )
throws BirtException
{
if ( textItem == null )
{
return null;
}
 
int angle = textItem.getRotationAngle( );
String text = textItem.getText( );
BufferedImage rotatedImage =
SwingGraphicsUtil.createRotatedTextImage( text, angle,
new Font( "Default", 0, 12 ) ); //$NON-NLS-1$
ByteArrayInputStream bis = null;
 
try
{
ImageIO.setUseCache( false );
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageOutputStream ios =
ImageIO.createImageOutputStream( baos );
ImageIO.write( rotatedImage, "png", ios ); //$NON-NLS-1$
ios.flush( );
ios.close( );
bis = new ByteArrayInputStream( baos.toByteArray( ) );
}
catch ( IOException e )
{
e.printStackTrace( );
}
return bis;
}
}
This implementation generates an image dynamically in the onRowSets( ) method. An in-memory stream returns the image content to the caller. The utility class that implements the key rotation rendering algorithm is shown in Listing 23‑13.
Listing 23‑13 SwingGraphicsUtil class
public class SwingGraphicsUtil
{
public static BufferedImage createRotatedTextImage(
String text, int angle, Font ft )
{
Graphics2D g2d = null;
try {
if ( text == null || text.trim( ).length( ) == 0 )
{
return null;
}
BufferedImage stringImage = new BufferedImage( 1, 1,
BufferedImage.TYPE_INT_ARGB );
g2d = (Graphics2D) stringImage.getGraphics( );
g2d.setFont( ft );
FontMetrics fm = g2d.getFontMetrics( );
Rectangle2D bounds = fm.getStringBounds( text, g2d );
TextLayout tl = new TextLayout( text, ft,
g2d.getFontRenderContext( ) );
g2d.dispose( );
g2d = null;
return createRotatedImage( tl, (int) bounds.getWidth( ),
(int) bounds.getHeight( ), angle );
}
catch ( Exception e )
{
e.printStackTrace( );
if ( g2d != null )
{
g2d.dispose( );
}
}
return null;
}
 
private static BufferedImage createRotatedImage( Object src,
int width, int height, int angle )
{
angle = angle % 360;
if ( angle < 0 )
{
angle += 360;
}
if ( angle == 0 )
{
return renderRotatedObject( src, 0, width, height, 0, 0);
}
else if ( angle == 90 )
{
return renderRotatedObject( src, -Math.PI / 2, height,
width, -width, 0 );
}
else if ( angle == 180 )
{
return renderRotatedObject( src, Math.PI, width, height,
-width, -height );
}
else if ( angle == 270 )
{
return renderRotatedObject( src, Math.PI / 2, height,
width, 0, -height );
}
else if ( angle > 0 && angle < 90 )
{
double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );
double cosTheta = Math.abs( Math.cos( angleInRadians ) );
double sineTheta = Math.abs( Math.sin( angleInRadians ));
int dW = (int) ( width * cosTheta + height * sineTheta );
int dH = (int) ( width * sineTheta + height * cosTheta );
return renderRotatedObject( src, angleInRadians, dW, dH,
-width * sineTheta * sineTheta,
width * sineTheta * cosTheta );
}
else if ( angle > 90 && angle < 180 )
{
double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );
double cosTheta = Math.abs( Math.cos( angleInRadians ) );
double sineTheta = Math.abs( Math.sin( angleInRadians ));
int dW = (int) ( width * cosTheta + height * sineTheta );
int dH = (int) ( width * sineTheta + height * cosTheta );
return renderRotatedObject( src, angleInRadians, dW, dH,
-( width + height * sineTheta * cosTheta ),
-height / 2 );
}
else if ( angle > 180 && angle < 270 )
{
double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );
double cosTheta = Math.abs( Math.cos( angleInRadians ) );
double sineTheta = Math.abs( Math.sin( angleInRadians ));
int dW = (int) ( width * cosTheta + height * sineTheta );
int dH = (int) ( width * sineTheta + height * cosTheta );
return renderRotatedObject( src, angleInRadians, dW, dH,
-( width * cosTheta * cosTheta ),
-( height + width * cosTheta * sineTheta ) );
}
else if ( angle > 270 && angle < 360 )
{
double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );
double cosTheta = Math.abs( Math.cos( angleInRadians ) );
double sineTheta = Math.abs( Math.sin( angleInRadians ));
int dW = (int) ( width * cosTheta + height * sineTheta );
int dH = (int) ( width * sineTheta + height * cosTheta );
return renderRotatedObject( src, angleInRadians, dW, dH,
( height * cosTheta * sineTheta ),
-( height * sineTheta * sineTheta ) );
}
return renderRotatedObject( src, 0, width, height, 0, 0 );
}
 
private static BufferedImage renderRotatedObject( Object src,
double angle, int width, int height, double tx, double ty )
{
BufferedImage dest = new BufferedImage( width, height,
BufferedImage.TYPE_INT_ARGB );
Graphics2D g2d = (Graphics2D) dest.getGraphics( );
g2d.setColor( Color.black );
g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON );
AffineTransform at = AffineTransform.getRotateInstance(
angle );
at.translate( tx, ty );
g2d.setTransform( at );
if ( src instanceof TextLayout )
{
TextLayout tl = (TextLayout) src;
tl.draw( g2d, 0, tl.getAscent( ) );
}
else if ( src instanceof Image )
{
g2d.drawImage( (Image) src, 0, 0, null );
}
g2d.dispose( );
return dest;
}
}
To test the report item, open a new Eclipse instance in the Report Design perspective. Create a report and insert a grid having nine cells. Insert a RotatedText item in each cell, as shown in Figure 23‑24, and use the Properties view to set a different rotation angle for each report item.
Figure 23‑24 Report layout having nine RotatedText items
A preview of the report as HTML is shown in Figure 23‑25.
Figure 23‑25 Preview in HTML