NEW - bug 245232: [Article] BIRT Extension Mechanism : Part 1
https://bugs.eclipse.org/bugs/show_bug.cgi?id=245232
diff --git a/Article-BIRT-ExtensionTutorial1/index.html b/Article-BIRT-ExtensionTutorial1/index.html
index 8bc267a..0e7e845 100644
--- a/Article-BIRT-ExtensionTutorial1/index.html
+++ b/Article-BIRT-ExtensionTutorial1/index.html
@@ -1,836 +1,593 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
-
-
-
-
-
-
-
-
- <title>*DRAFT* BIRT Extension Mechanism : Part 1</title>
+ <title>BIRT Extension Mechanism : Part 1</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
-
-
-
-
-
<link href="../article.css" type="text/css" rel="stylesheet">
</head>
-
<body>
-
-
-<h1>*DRAFT* BIRT Extension Mechanism : Part 1</h1>
-
-
+<h1>BIRT Extension Mechanism : Part 1</h1>
<div class="summary">
<h2>Abstract</h2>
-
-
<p>This article introduces the extension mechanism of BIRT report
model, engine and designer, shows how to create custom extended report items
step by
step.</p>
-
-
<div class="author">By Zhiqiang
Qian, Actuate Corporation</div>
-
-
<div class="copyright">Copyright © 2008 Actuate Corporation.</div>
-
-
<div class="date">August 7, 2008</div>
-
-
</div>
-
-
<div class="content">
<h2>Introduction</h2>
-
-
-<p>For many people, <a href="http://eclipse.org/birt">BIRT(Business
-Intelligence and Reporting Tools)</a> may
-only mean a regular reporting tool. But in fact, the real strength of
-BIRT is not only its built-in reporting functionality, but also
+<p>For many people, <a href="http://eclipse.org/birt">BIRT (Business
+Intelligence and Reporting Tools)</a> may be
+only a reporting tool. But in fact, the real strength of
+BIRT is not only its built-in reporting functionality, but also
its
-extension capabilities. By extensions, user can easily extend the
+extension capabilities. Using extensions, a user can easily extend the
functionality of BIRT, creating custom extended report items, adding custom
-emitters, and even having custom editor pages. The powerfulness of extensions
-enables user to create
+emitters, and even having custom editor pages. The power of extensions
+enables the user to create
custom reporting features that can meet very specific requirements. In
this
-article, we will explorer how to leverage the BIRT extension mechanism to
-extend the BIRT capabilities. The given samples will
-cover most of the essential extension points that provided by BIRT report
+article, we explore how to leverage the BIRT extension mechanism to
+extend the BIRT capabilities. The provided samples
+cover most of the essential extension points that provided by BIRT report
model, engine and designer.<br>
-
-
</p>
-
-
<h2>The Basics</h2>
-
-
<p>BIRT already contains the basic Label and Text report items,
-but these items can only show horizontal text. Sometimes, user
+but these items can only show horizontal text. Sometimes, user
would
-like to show angled text, in this case, alternative solution is needed.
-In following sections, we are introducing how to implement a
+like to show angled text; in this case, alternative solution is needed.
+In following sections, we introduce how to implement a
RotatedText
-extended report item through BIRT extensions, and how to enhance
+extended report item through BIRT extensions, and how to enhance
the functionality step by step by implementing more extension points.</p>
-
-
<p>Note: This example is a modified version based
-on RotatedText sample on BIRT CVS, the original code
-can be retrieved <a href="http://dev.eclipse.org/viewcvs/index.cgi/?root=BIRT_Project">here.</a></p>
+on the <code>RotatedText</code> sample on BIRT CVS, the original code
+can be retrieved <a href="http://dev.eclipse.org/viewcvs/index.cgi/?root=BIRT_Project">in Eclipse CVS.</a></p>
-
-
-<p>To implement an extended report item, basically we need look at following extension points:</p>
-
-
+<p>To implement an extended report item, we need look at the following extension points:</p>
<ul>
-
-
-
<li>org.eclipse.birt.report.model.reportItemModel</li>
-
-
-
<li>org.eclipse.birt.report.designer.ui.reportitemUI</li>
-
-
-
<li>org.eclipse.birt.report.engine.reportitemPresentation</li>
-
-
-
</ul>
-
-
<br>
-
-
<img style="width: 514px; height: 156px;" alt="" src="images/extensions.png"><br>
+<p>These three extensions provide the basics to implement an extended
+report item. In brief,
+<code>org.eclipse.birt.report.model.reportItemModel</code> provides the report
+model extensibility, <code>org.eclipse.birt.report.designer.ui.reportitemUI</code>
+provides the report designer extensibility, and
+<code>org.eclipse.birt.report.engine.reportitemPresentation</code> provides the
+report engine extensibility.</p>
-<p>These three extensions are the basics to implement an extended
-report item. In brief,
-"org.eclipse.birt.report.model.reportItemModel" provides the report
-model extensibility, "org.eclipse.birt.report.designer.ui.reportitemUI"
-provides the report designer extensibility and
-"org.eclipse.birt.report.engine.reportitemPresentation" provides the
-report engine extensibility. Now we will introduce them one by one.</p>
+<h3>org.eclipse.birt.report.model.reportItemModel</h3>
-
-
-<h3>1) org.eclipse.birt.report.model.reportItemModel</h3>
-
-
-
-<p>This extension point is provided by BIRT Report Model, normally user
+<p>This extension point is provided by the BIRT Report Model. Normally the user
uses this extension point to define the extended report item model.</p>
-
-
<img style="width: 1036px; height: 224px;" alt="" src="images/model-extension.png">
-<p>First we need define a new extended report item model
-extension as "RotatedText", this name is the identifier
+<p>First we need define a new report item model
+extension. To achieve this, we create a "reportItem" element under the extension and specify the <code>extensionName</code> property as "RotatedText".
+The extension name is the identifier
for the extended item, it's also the only symbol that connects between the report model
-and designer extensions.</p>
+and engine/designer extensions.</p>
<p>Then we define the model factory class as
-"org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextItemFactory".
+<code>org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextItemFactory</code>.
This factory class will be used to create and initialize the
-IReportItem instance, usually this class can also implement an
-additional IMessages interface to provide localization support:</p>
+<code>IReportItem</code> instance, as well as providing optional localization support:</p>
+<pre>public class RotatedTextItemFactory extends ReportItemFactory
+{
+ public IReportItem newReportItem( DesignElementHandle modelHandle )
+ {
+ if ( modelHandle instanceof ExtendedItemHandle && RotatedTextItem.EXTENSION_NAME.equals( ( (ExtendedItemHandle) modelHandle ).getExtensionName( ) ) )
+ {
+ return new RotatedTextItem( (ExtendedItemHandle) modelHandle );
+ }
+ return null;
+ }
+ public IMessages getMessages( )
+ {
+ // TODO implement this to support localization
+ return null;
+ }
+}</pre>
-<pre style="font-family: monospace;">public class RotatedTextItemFactory extends ReportItemFactory<br>{<br> public IReportItem newReportItem( DesignElementHandle modelHanlde )<br> {<br> if ( modelHanlde instanceof ExtendedItemHandle && RotatedTextItem.EXTENSION_NAME.equals( ( (ExtendedItemHandle) modelHanlde ).getExtensionName( ) ) )<br> {<br> return new RotatedTextItem( (ExtendedItemHandle) modelHanlde );<br> }<br> return null;<br> }<br><br> public IMessages getMessages( )<br> {<br> // TODO implement this to support localization<br> return null;<br> }<br>}<br></pre>
+<p>From the "reportItem" properties page, we can see some other settings like "defaultStyle", "extendsFrom", etc.
+They can be used to control more capabilities of the extended item. As we are not using them in this example,
+we just omit them here.</p>
-
-
-<p>There are some extra properties here that are not used by this example, we just introduce them briefly:</p>
-
-
+<p>Here we define two new properties for the <code>RotatedText</code> extended report item:</p>
<ul>
+ <li><em>rotationAngle</em>, the rotation angle, integer type, default as 0.</li>
+<br><img style="width: 1001px; height: 279px;" alt="" src="images/rotationangle-property.png">
- <li>defaultStyle</li>
+ <li><em>text</em>, the text content, string type, default as "Rotated Text".</li>
-
-
-
-
- <ul>
-
-
-
- <li>specify the default style name, this will be used by BIRT
-to find and match the default style for this extended report item
-automatically.</li>
-
-
-
-
-
- </ul>
-
-
-
- <li>isNameRequired</li>
-
-
-
-
-
- <ul>
-
-
-
- <li>specify if the name is required for this extended report item.</li>
-
-
-
-
-
- </ul>
-
-
-
- <li>displayNameID</li>
-
-
-
-
-
- <ul>
-
-
-
- <li>specify the display name ID for this extended report item, the
-ID will be passed to IMessags interface to get the localized
-extended report item name.</li>
-
-
-
-
-
- </ul>
-
-
-
- <li>extendsFrom</li>
-
-
-
-
-
- <ul>
-
-
-
- <li>specify the name of the report item that this extended report
-item extends from, if this is not specified, the item will by default extend from
-"ExtendedItem".</li>
-
-
-
-
-
- </ul>
-
-
-
- <li>hasStyle</li>
-
-
-
-
-
- <ul>
-
-
-
- <li>specify if this extended report item supports styled properties, for some non-UI report items, it can be set to "false".</li>
-
-
-
-
-
- </ul>
-
-
+<br><img style="width: 1000px; height: 274px;" alt="" src="images/text-property.png">
</ul>
+<p>We can see in the properties page there are also some settings that we didn't touch. Normally they are for more complex property types and
+are out of the scope of this article, so we just omit them here.</p>
-<p>Now we need define two new properties for the RotatedText extended report item:</p>
-
-
-
-<ul>
-
-
-
- <li>rotationAngle </li>
-
-
-
-</ul>
-
-
-
-
-
-the rotation angle, integer type, default as 0.<br>
-
-
-
-<img style="width: 1001px; height: 279px;" alt="" src="images/rotationangle-property.png">
-<ul>
-
-
-
- <li>text</li>
-
-
-
-</ul>
-
-
-
-
-
-the text content, string type, default as "Rotated Text".<br>
-
-
-
-<img style="width: 1000px; height: 274px;" alt="" src="images/text-property.png"><br>
-
-
-
-<br>
-
-
-
-<p>We can see a lot of things in the
-property setting page that we haven't used yet, normally they are for more complex property types, we
-just skip them here.<br>
-
-
-
-<br>
-Once we completed the model definition, we need give a basic IReportItem
+<p>
+Once we completed the model definition, we need give a <code>IReportItem</code>
implementation. This class is used to encapsulate the extended report
-item model, providing some convenient property accessing APIs:</p>
+item model, and providing some convenient property accessing APIs:</p>
+
+<pre>public class RotatedTextItem extends ReportItem
+{
+ public static final String EXTENSION_NAME = "RotatedText"; //$NON-NLS-1$
+ public static final String TEXT_PROP = "text"; //$NON-NLS-1$
+ public static final String ROTATION_ANGLE_PROP = "rotationAngle"; //$NON-NLS-1$
+
+ private ExtendedItemHandle modelHandle;
+
+ RotatedTextItem( ExtendedItemHandle modelHandle )
+ {
+ this.modelHandle = modelHandle;
+ }
+
+ public String getText( )
+ {
+ return modelHandle.getStringProperty( TEXT_PROP );
+ }
+
+ public int getRotationAngle( )
+ {
+ return modelHandle.getIntProperty( ROTATION_ANGLE_PROP );
+ }
+
+ public void setText( String value ) throws SemanticException
+ {
+ modelHandle.setProperty( TEXT_PROP, value );
+ }
+
+ public void setRotationAngle( int value ) throws SemanticException
+ {
+ modelHandle.setProperty( ROTATION_ANGLE_PROP, value );
+ }
+}</pre>
+
+<p>We can see most of the methods here are just getter/setters. For
+a simple extended report item, this is normally enough.</p>
+
+<h3>org.eclipse.birt.report.designer.ui.reportitemUI</h3>
+
+<p>This extension point is provided by BIRT Designer, the user uses this
+extension point to define the UI behavior for extended report
+items.</p>
+
+<img style="width: 1001px; height: 233px;" alt="" src="images/ui-extension.png">
+<!-- WTB I realize that the image above provides context for the comment below, but
+I think that I'd like to see some more descriptive text around this step. -->
-<pre>public class RotatedTextItem extends ReportItem<br>{<br> public static final String EXTENSION_NAME = "RotatedText"; //$NON-NLS-1$<br> public static final String TEXT_PROP = "text"; //$NON-NLS-1$<br> public static final String ROTATION_ANGLE_PROP = "rotationAngle"; //$NON-NLS-1$<br><br> private ExtendedItemHandle modelHandle;<br><br> RotatedTextItem( ExtendedItemHandle modelHandle )<br> {<br> this.modelHandle = modelHandle;<br> }<br><br> public String getText( )<br> {<br> return modelHandle.getStringProperty( TEXT_PROP );<br> }<br><br> public int getRotationAngle( )<br> {<br> return modelHandle.getIntProperty( ROTATION_ANGLE_PROP );<br> }<br><br> public void setText( String value ) throws SemanticException<br> {<br> modelHandle.setProperty( TEXT_PROP, value );<br> }<br><br> public void setRotationAngle( int value ) throws SemanticException<br> {<br> modelHandle.setProperty( ROTATION_ANGLE_PROP, value );<br> }<br>}<br></pre>
+<p>
+First we need create a "model" element under the extension. This is to bind the Designer extension with the Model extension. To do
+this, we specify the <code>extensionName</code> property as "RotatedText", which exactly matches the model extension name.</p>
-
-
-<p>We can see most of the methods here are just getter/setters. For
-simple extended report item, this is normally already enough.</p>
-
-
-
-<h3>2) org.eclipse.birt.report.designer.ui.reportitemUI</h3>
-
-
-
-<p>This extension point is provided by BIRT Designer, user uses this
-extension point to define the UI behavior for extended report
-items.<br>
-
-
-
-<br>
-
-
-
-<img style="width: 1001px; height: 233px;" alt="" src="images/ui-extension.png"><br>
-
-
-
-<br>
-
-First we need bind the Designer extension with Model extension, to do
-this, we simply specify the extension name as "RotatedText".</p>
-
-
-
-<p>Next step, we specify some settings for this extended report item:</p>
-
-
+<p>The next step, we create some other elements like "palette", "editor" and "outline" under the extension, and specify the detailed settings for this extended report item as following:</p>
<ul>
-
-
-
- <li>Palette Settings</li>
-
-
-
-
-
+ <li>Palette</li>
<ul>
-
-
-
- <li>specify the icon to be shown in Palette view, and the
-palette category as well. If the category is not specified, it will use
-the default "Report Items" category.</li>
-
-
-
-
-
+ <li>Specifies the icon to be shown in Palette view, as well as the
+palette category. If the category is not specified, the default "Report Items" category will be used.</li>
</ul>
-
-
-
</ul>
-
-
<img style="width: 495px; height: 111px;" alt="" src="images/palette-detail.png"><br>
-
-
<br>
-
-
<ul>
-
-
-
- <li>Editor Settings</li>
-
-
-
-
-
+ <li>Editor</li>
<ul>
-
-
-
- <li>specify the report item visibility in different editor pages, and whether resizing control should be enabled. </li>
-
-
-
-
-
+ <li>Specifies the report item visibility in different editor pages, and whether the resizing control is enabled.</li>
</ul>
-
-
-
</ul>
-
-
<img style="width: 495px; height: 133px;" alt="" src="images/editor-detail.png"><br>
-
-
<br>
-
-
<ul>
+ <li>Outline</li>
-
-
- <li>Outline Settings</li>
-
-
-
-
-
<ul>
-
-
-
- <li>specify the icon to be shown in Outline view, normally this is the same as in Palette view.</li>
-
-
-
-
-
+ <li>Specifies the icon to be shown in Outline view. Normally this is the same as the one in Palette view.</li>
</ul>
-
-
</ul>
-
-
<img style="width: 497px; height: 90px;" alt="" src="images/icon-detail.png"><br>
<p>
+The last step, we need specify the UI provider for this extended report item.</p>
-The last step, specify the implementation class for UI provider:</p>
-
-
-
-<img style="width: 498px; height: 94px;" alt="" src="images/labelui-detail.png"><br>
-
-
-
-<p>The UI provider defines how to display and interact with the
+<p>The UI provider defines how to display and interact with the
extended report item within the editor. BIRT designer support three types of
basic UI providers:</p>
-
-
<ul>
-
-
<li>Label UI Provider</li>
-
-
-
-
-
<ul>
-
-
-
- <li>user implements the IReportItemLabelProvider interface, it simply just requires an text content.</li>
-
-
-
-
-
+ <li>The user implements the <code>IReportItemLabelProvider</code> interface, which simply manipulates and displays a text content.</li>
</ul>
-
-
<li>Image UI Provider</li>
-
-
-
-
-
<ul>
-
-
-
- <li>user implements the IReportItemImageProvider interface, it simply just requires an image content.</li>
-
-
-
-
+ <li>The user implements the <code>IReportItemImageProvider</code> interface, which simply manipulates and displays an image content.</li>
</ul>
-
-
-
+
<li>Figure UI Provider</li>
-
-
-
-
-
<ul>
-
-
-
- <li>user implements the IRportItemFigureProvider, This provider uses Figure interface from <a href="http://www.eclipse.org/gef/">GEF</a>, providing more flexibility and interactivity support.</li>
-
-
-
-
-
+ <li>The user implements the <code>IRportItemFigureProvider</code>, which uses the Figure interface from <a href="http://www.eclipse.org/gef/">GEF</a>, providing more flexibility and interactivity support.</li>
</ul>
-
-
</ul>
-
<br>
+<p>In this example, we choose to implement the simplest Label UI
+Provider first, in subsequent examples, we will introduce other types of UI providers.</p>
-<p>In this example, we choose to implement the simplest Label UI
-Provider first, in consequent examples, we may introduce other type of UI providers.</p>
+<p>To register the Label UI provider, we create a "reportItemLabelUI" element under the extension, and specify the implementor class, which must implement <code>IReportItemLabelProvider</code> interface.</p>
+<img style="width: 498px; height: 94px;" alt="" src="images/labelui-detail.png"><br>
<p>
-Our Label UI Provider implementation class is
-"org.eclipse.birt.sample.reportitem.rotatedText.RotatedTextLabelUI",
-the code looks very simple:</p>
+Here our Label UI Provider implementor class is <code>org.eclipse.birt.sample.reportitem.rotatedText.RotatedTextLabelUI</code>,
+the code is very simple:</p>
+<pre>public class RotatedTextLabelUI implements IReportItemLabelProvider
+{
+ public String getLabel( ExtendedItemHandle handle )
+ {
+ try
+ {
+ IReportItem item = handle.getReportItem( );
+ if ( item instanceof RotatedTextItem )
+ {
+ return ( (RotatedTextItem) item ).getText( );
+ }
+ }
+ catch ( ExtendedElementException e )
+ {
+ e.printStackTrace( );
+ }
+ return null;
+ }
+}</pre>
-
-<pre>public class RotatedTextLabelUI implements IReportItemLabelProvider<br>{<br> public String getLabel( ExtendedItemHandle handle )<br> {<br> try<br> {<br> IReportItem item = handle.getReportItem( );<br> if ( item instanceof RotatedTextItem )<br> {<br> return ( (RotatedTextItem) item ).getText( );<br> }<br> }<br> catch ( ExtendedElementException e )<br> {<br> e.printStackTrace( );<br> }<br> return null;<br> }<br>}</pre>
-
-
-
-<p>You can see what it does is just reading the property from model and returning a text value.<br>
-
-
+<p>You can see what it really does is just reading the text property value from model and returning as a string.<br>
<br>
-
-Once we completed the designer extension, we can run the desginer first and have a glance for what we already have now:<br>
-
-
+After we completed the designer extension, we can run the desginer first and check what we already have now:<br>
<br>
-
-
-OK, the Palette view already contains the new RotatedText extended report item.</p>
-
-
+OK, the Palette view now contains the new <code>RotatedText</code> extended report item.</p>
<img style="width: 341px; height: 408px;" alt="" src="images/palette.png"> <br>
-
-
-<p>Right-click in the editor, we can see the RotatedText item is also available in the context menu.</p>
-
-
+<p>Right-click anywhere in the editor, we can see the <code>RotatedText</code> item is also available in the "Insert" context menu.</p>
<img style="width: 487px; height: 395px;" alt="" src="images/layout.png"><br>
-
-
-<p>Now try insert one RotatedText item to report, in Outline view, we can see the new RotatedText node.<br>
-
-
+<p>Now try insert one RotatedText item in layout, in Outline view, we can see it also appears.<br>
</p>
-
-
<img style="width: 347px; height: 438px;" alt="" src="images/outline.png"><br>
-
-
-<p>Select the RotatedText item, open the Properties view, it immediately
+<p>Select the RotatedText item, open the <em>Properties</em> view, it immediately
lists all the properties that belong to this extended report item. Among
-them, we can see "Rotation Angle" and "Text
-Content" as well as other inherited properties. User can edit the
-property value in this view.
+them, we can see the "Rotation Angle" and "Text
+Content" as well as other inherited properties. By now, the user can edit these
+property values through this view.
</p>
-
-
<img style="width: 728px; height: 346px;" alt="" src="images/properties.png"><br>
+<p>Now the designer extension is done, let's continue to the Report Engine extension.</p>
-<p>Now designer extension is done, let's step to the Report Engine extension.</p>
+<h3>org.eclipse.birt.report.engine.reportitemPresentation</h3>
-
-
-<h3>3) org.eclipse.birt.report.engine.reportitemPresentation</h3>
-
-
-
-<p>This extension point is provided by BIRT Report Engine, user uses
+<p>This extension point is provided by BIRT Report Engine, the user uses
this extension point to define the presentation behavior of the
extended report item.<br>
-
-
</p>
-
-
<div style="text-align: left;"><img style="width: 1003px; height: 166px;" alt="" src="images/engine-extension.png"><br>
-
-
-<p>First we also need specify the extension name as "RotatedText" to link it
-with Model extension, and specify the presentation implementation
-class
-as
-"org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextPresentationImpl".
-The presentation implementation class need implement the
-IReportItemPresentation interface.<br>
-
-
+<p>First we also need bind the engine extension with model extension. To achieve this, we create a "reportItem" element under the engine extension and
+specify the <code>name</code> property as "RotatedText", also we specify the presentation implementor class
+as <code>org.eclipse.birt.sample.reportitem.rotatedtext.RotatedTextPresentationImpl</code>.
+The presentation implementor class must implement the
+<code>IReportItemPresentation</code> interface.<br>
<br>
+Our plan for implementing the rotated text effect is to create an image
+dynamically, in this image, we use <em>Swing</em> graphics API to render the
+rotated text.</p>
-Our plan to implement the rotated text effect is to create an image
-dynamically, in this image, we use SWING graphics API to render the
-rotated text. </p>
+<p>Here is the code that we need:</p>
-<p>Here is the code that all we need:</p>
+<pre>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<br>
+ {
+ 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;
+ }
+}</pre>
-
-
-<pre>public class RotatedTextPresentationImpl extends ReportItemPresentationBase<br>{<br> private RotatedTextItem textItem;<br><br> public void setModelObject( ExtendedItemHandle modelHandle )<br> {<br> try<br> {<br> textItem = (RotatedTextItem) modelHandle.getReportItem( );<br> }<br> catch ( ExtendedElementException e )<br> {<br> e.printStackTrace( );<br> }<br> }<br><br> public int getOutputType( )<br> {<br> return OUTPUT_AS_IMAGE;<br> }<br><br> public Object onRowSets( IRowSet[] rowSets ) throws BirtException<br> {<br> if ( textItem == null )<br> {<br> return null;<br> }<br><br> int angle = textItem.getRotationAngle( );<br> String text = textItem.getText( );<br> BufferedImage rotatedImage = SwingGraphicsUtil.createRotatedTextImage( text, angle, new Font( "Default", 0, 12 ) ); //$NON-NLS-1$<br> ByteArrayInputStream bis = null;<br><br> try<br> {<br> ImageIO.setUseCache( false );<br> ByteArrayOutputStream baos = new ByteArrayOutputStream( );<br> ImageOutputStream ios = ImageIO.createImageOutputStream( baos );<br> ImageIO.write( rotatedImage, "png", ios ); //$NON-NLS-1$<br> ios.flush( );<br> ios.close( );<br> bis = new ByteArrayInputStream( baos.toByteArray( ) );<br> }<br> catch ( IOException e )<br> {<br> e.printStackTrace( );<br> }<br> return bis;<br> }<br>}<br></pre>
-
-
-
-<p>The logic is pretty straightforward, the main part is to
-generate an image dynamically in the onRowSets() method. To return
-the image, we simply create an in-memory stream to hold the image
-content.</p>
-
-
+<p>The logic is actually pretty straightforward, the main part is to
+generate an <code>Image</code> dynamically in the <code>onRowSets()</code> method. To return
+the image content, we simply create an in-memory stream and put the image content in it.</p>
<p>Here is the utility class that implements the key rotation rendering algorithm:</p>
-
-
-<pre>public class SwingGraphicsUtil<br>{<br> public static BufferedImage createRotatedTextImage( String text, int angle, Font ft )<br> {<br> Graphics2D g2d = null;<br> try<br> {<br> if ( text == null || text.trim( ).length( ) == 0 )<br> {<br> return null;<br> }<br> BufferedImage stringImage = new BufferedImage( 1, 1, BufferedImage.TYPE_INT_ARGB );<br> g2d = (Graphics2D) stringImage.getGraphics( );<br> g2d.setFont( ft );<br> FontMetrics fm = g2d.getFontMetrics( );<br> Rectangle2D bounds = fm.getStringBounds( text, g2d );<br> TextLayout tl = new TextLayout( text, ft, g2d.getFontRenderContext( ) );<br> g2d.dispose( );<br> g2d = null;<br> return createRotatedImage( tl, (int) bounds.getWidth( ), (int) bounds.getHeight( ), angle );<br> }<br> catch ( Exception e )<br> {<br> e.printStackTrace( );<br> if ( g2d != null )<br> {<br> g2d.dispose( );<br> }<br> }<br> return null;<br> }<br><br> private static BufferedImage createRotatedImage( Object src, int width, int height, int angle )<br> {<br> angle = angle % 360;<br> if ( angle < 0 )<br> {<br> angle += 360;<br> }<br> if ( angle == 0 )<br> {<br> return renderRotatedObject( src, 0, width, height, 0, 0 );<br> }<br> else if ( angle == 90 )<br> {<br> return renderRotatedObject( src, -Math.PI / 2, height, width, -width, 0 );<br> }<br> else if ( angle == 180 )<br> {<br> return renderRotatedObject( src, Math.PI, width, height, -width, -height );<br> }<br> else if ( angle == 270 )<br> {<br> return renderRotatedObject( src, Math.PI / 2, height, width, 0, -height );<br> }<br> else if ( angle > 0 && angle < 90 )<br> {<br> double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );<br> double cosTheta = Math.abs( Math.cos( angleInRadians ) );<br> double sineTheta = Math.abs( Math.sin( angleInRadians ) );<br> int dW = (int) ( width * cosTheta + height * sineTheta );<br> int dH = (int) ( width * sineTheta + height * cosTheta );<br> return renderRotatedObject( src, angleInRadians, dW, dH, -width * sineTheta * sineTheta, width * sineTheta * cosTheta );<br> }<br> else if ( angle > 90 && angle < 180 )<br> {<br> double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );<br> double cosTheta = Math.abs( Math.cos( angleInRadians ) );<br> double sineTheta = Math.abs( Math.sin( angleInRadians ) );<br> int dW = (int) ( width * cosTheta + height * sineTheta );<br> int dH = (int) ( width * sineTheta + height * cosTheta );<br> return renderRotatedObject( src, angleInRadians, dW, dH, -( width + height * sineTheta * cosTheta ), -height / 2 );<br> }<br> else if ( angle > 180 && angle < 270 )<br> {<br> double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );<br> double cosTheta = Math.abs( Math.cos( angleInRadians ) );<br> double sineTheta = Math.abs( Math.sin( angleInRadians ) );<br> int dW = (int) ( width * cosTheta + height * sineTheta );<br> int dH = (int) ( width * sineTheta + height * cosTheta );<br> return renderRotatedObject( src, angleInRadians, dW, dH, -( width * cosTheta * cosTheta ), -( height + width * cosTheta * sineTheta ) );<br> }<br> else if ( angle > 270 && angle < 360 )<br> {<br> double angleInRadians = ( ( -angle * Math.PI ) / 180.0 );<br> double cosTheta = Math.abs( Math.cos( angleInRadians ) );<br> double sineTheta = Math.abs( Math.sin( angleInRadians ) );<br> int dW = (int) ( width * cosTheta + height * sineTheta );<br> int dH = (int) ( width * sineTheta + height * cosTheta );<br> return renderRotatedObject( src, angleInRadians, dW, dH, ( height * cosTheta * sineTheta ), -( height * sineTheta * sineTheta ) );<br> }<br> return renderRotatedObject( src, 0, width, height, 0, 0 );<br> }<br><br> private static BufferedImage renderRotatedObject( Object src, double angle, int width, int height, double tx, double ty )<br> {<br> BufferedImage dest = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB );<br> Graphics2D g2d = (Graphics2D) dest.getGraphics( );<br> g2d.setColor( Color.black );<br> g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );<br> g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );<br> AffineTransform at = AffineTransform.getRotateInstance( angle );<br> at.translate( tx, ty );<br> g2d.setTransform( at );<br> if ( src instanceof TextLayout )<br> {<br> TextLayout tl = (TextLayout) src;<br> tl.draw( g2d, 0, tl.getAscent( ) );<br> }<br> else if ( src instanceof Image )<br> {<br> g2d.drawImage( (Image) src, 0, 0, null );<br> }<br> g2d.dispose( );<br> return dest;<br> }<br>}<br></pre>
-
-
+<pre>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;
+ }
+}</pre>
<h2>The Result</h2>
-
-
-<p>Now all required extension points are done, let's have a test.<br>
-
-
+<p>Now all required extension points are ready, let's have an integrated test.<br>
<br>
-
-Create a report that contains some RotatedText items first:<br>
-
-
+Create a report and insert some RotatedText items first:<br>
</p>
-
-
<img style="width: 865px; height: 273px;" alt="" src="images/design-layout.png"><br>
-
-
-<p>Here we inserted quite a few RotatedText items with different angle settings.<br>
-
-
+<p>Here we have inserted quite a few RotatedText items that with different angle settings.<br>
<br>
-
Preview the report as HTML, it results like this:<br>
-
-
</p>
-
-
<img style="width: 703px; height: 218px;" alt="" src="images/html-output.png"><br>
-
-
<p>Preview the report as PDF, it results like this:<br>
-
-
</p>
-
-
<img style="width: 757px; height: 482px;" alt="" src="images/pdf-output.png"><br>
-
-
<br>
-
-
-<p>Done! So far we've successfully implemented a RotatedText extended
+<p>Done! So far we've successfully implemented a <code>RotatedText</code> extended
report item for BIRT, and integrated it with Designer and Report Engine.<br>
-
-
</p>
-
-
</div>
-
-
<h2>Summary</h2>
-
-
-<p>BIRT provides very good extension mechanism, which allows user
+<p>BIRT provides very good extension mechanism, which allows user
to create custom reporting features. This article introduced how to write
a custom RotatedText extended report item for BIRT.</p>
-
-
<h2>Resources</h2>
+<p>[1] <a href="extension-tutorial-1.zip">The complete source code</a>: you can extract and import it into Eclipse workspace as a plugin project directly</p>
+<p>[2] BIRT Official Site: <a href="http://eclipse.org/birt">http://eclipse.org/birt</a></p>
-<p>[1] <a href="extension-tutorial-1.zip">The complete source code</a>: you can extract and import it into Eclipse workspace as a plugin project directly</p>
-
-
-
-<p>[2] BIRT Official Site: <a href="http://eclipse.org/birt">http://eclipse.org/birt</a></p>
-
-
-
-<p>[3] How to access Eclipse CVS: <a href="http://wiki.eclipse.org/CVS_Howto">http://wiki.eclipse.org/CVS_Howto</a></p>
-
-
+<p>[3] How to access Eclipse CVS: <a href="http://wiki.eclipse.org/CVS_Howto">http://wiki.eclipse.org/CVS_Howto</a></p>
<p><br>
-
-
<br>
-
-
</p>
-
-
<br>
-
-
</div>
-
-
</body>
</html>