Thursday, July 19, 2007

Styles Revisited

Its been a while, but after some family commitments, I am finally back at it with BIRT. In April of 2006, I created a post discussing how Styles could be used within BIRT. With the release of 2.2, it is time to revisit CSS Styles in BIRT. The biggest change is that you can now link to a CSS style sheet, the styles do not need to be imported into the report. This is a HUGE improvement.

To take advantage of this feature you go to the outline view, right click on styles and get the following dialog.


The new option allows you to "Use CSS File". When you select this file a dialog that enables you to select a file.


Once you have attached the CSS file, you will then have access to any of the classes in the CSS file to use as styles within your report. But this is just the beginning. All of the major objects and positions within a BIRT report have a default style associated with them. By modifying the default style settings you can change the look of your report without having to set any styles manually.

The best place to start with the pre-defined styles is to start with the visual objects that make up the report the controls. Each control type has a default style associated with it.
  • text
  • label
  • text
  • data
  • image
  • text-data

So adding the following entry in your style sheet will change the default behavior of all text controls to be orange.
.text{
color: orange;
}

In reality, one should be very careful with how many changes are made to the lowest level styles. Changing a property for a basic control style, will change the style for all instance of that control, unless the control specifically over-rides the property. So if you want all of your text to be orange, by all means modify the base control classes. If on the other hand, you want to modify report style by position within the report, then read on. In other words, if you want the header for all of your tables to look one way and the detail to look another, BIRT provides default classes that control that behavior.

Before moving into the full array of BIRT default classes, I want to expose the CSS properties that BIRT supports. The following table documents the properties and the default property values that BIRT uses.

CSS Property Values | Default | Inherit
background-attachment scroll | fixed | inherit

<color>| transparent | inherit
background-image <uri> | none | inherit
background-position [ [ <percentage> | <length> | left | center | right ] [ <percentage> | <length> | top | center | bottom ]? ] | [ [ left | center | right ] || [ top | center | bottom ] ] | inherit
background-repeat repeat | repeat-x | repeat-y | no-repeat | inherit
border-top-color , border-right-color , border-bottom-color , border-left-color <color> | transparent | inherit
border-top-style , border-right-style , border-bottom-style , border-left-style <border-style> | inherit
border-top-width , border-right-width , border-bottom-width , border-left-width <border-width> | inherit
color <color> | inherit
display inline | block | list-item | run-in | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | none | inherit
font-family [[ <family-name> | <generic-family> ] [, <family-name> | <generic-family>]* ] | inherit
font-size <absolute-size> | <relative-size> |<length> | <percentage>| inherit
font-style normal | italic | oblique | inherit
font-variant normal | small-caps |inherit
font-weight normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit
letter-spacing normal | <length> | inherit
line-height normal |<number> | <length> | <percentage> | inherit
margin-right , margin-left <margin-width> | inherit
margin-top , margin-bottom <margin-width> | inherit
orphans <integer> | inherit
padding-top , padding-right , padding-bottom , padding-left <padding-width> | inherit
page-break-after auto | always | avoid | left | right | inherit
page-break-before auto | always | avoid | left | right | inherit
page-break-inside avoid | auto | inherit
text-indent <length> | <percentage> | inherit
text-align left | right | center | justify | inherit
text-decoration none | [ underline || overline || line-through || blink ] | inherit
text-transform capitalize | uppercase | lowercase | none | inherit
white-space normal | pre | nowrap | pre-wrap | pre-line | inherit
widows <integer> | inherit
word-spacing normal | <length>|inherit
vertical-align baseline | sub | super | top | text-top | middle | bottom | text-bottom | <percentage> | <length> | inherit

Most of you are probably familiar with the standard HTML cascade:
Table
tr
td

A td element will inherit from either the table or the row if it has not defined its own value. If you do specify a property value for the td selector, than it will over-ride the values set at both the row and table levels.

BIRT uses its own cascade precedence. Since each of the major objects and functional positions is assigned a default style. The next table shows the precedence used for the BIRT default styles.

Controls

data

image

label

text

text-data
report

page

grid

list

list-header

list-detail

list-footer

list-group-footer-1

. . .

list-group-footer-9

list-group-header-1

. . .

list-group-header-9

table

table-header

table-header-cell

table-detail

table-detail-cell

table-footer

table-footer-cell

table-group-header-1

table-group-header-cell

. . .

table-group-header-9

table-group-footer-1

table-group-footer-cell

. . .

table-group-footer-9
TOC-level-0
. . .
TOC-level-9

So if you want to change the font-family for all of the controls within your report, then you can use the following within your CSS document.

.report{
font-family: sans-serif;
}

If you want the tables in the report to use italics and a serif font, then you would add:
font-family: sans-serif;

.table{
font-style: italics;
font-family: serif;
}


As you can see, you can control the behavior for headers, detail sections, and footers for list and tables. In addition, each of the group headings can use its own style up to 9 levels deep. In the previous article, I provided a CSS document that was setup to take advantage of this feature. Unfortunately, the Default Style Names have changed between 2.1 and 2.2. I have revised the style-sheet to work with the new default style names.

.report {
font-family : sans-serif;
font-size : medium;
font-weight : bold;
color : #FFFFE0;
}
.table-group-header-1 {

background : #0B285B;
border-bottom : double;
border-top : solid;
border-top-width : thin;
border-color : #FFF8DC;
}
.table-group-header-2 {
background : #13449B;
border-bottom : double;
border-top : solid;
border-top-width : thin;
border-color : #FFF8DC;
font-size : small;
}
.table-header {
background : #6495ED;
border-bottom : double;
border-top : solid;
border-top-width : thin;
border-color : #483D8B;
}
.table-detail {
background : #F0F8FF;
font-size : small;
font-weight : bold;
color : #4682B4;
}
.table-footer {
background : #6495ED;
border-top : double;
border-bottom : solid;
border-bottom-width : thin;
border-color : #483D8B;
font-size : x-small;
}

I am hopeful that in the next edition, I can take a look at adding a bit of code to the report that will do more to automatically assign styles to reports, even if you are using custom styles. I will let you know when I get it done.

45 comments:

Anonymous said...

Hi,

i use tomcat 5.5.23 with birt 2.2 to generate report.
one thing i found strenge is although i set the OutputStream using RenderOption.setOutputStream to a File in another location, the generated report will be always in tomcat_home/bin order and an another report with the same name but with size 0KB in defined location.

is it a bug or have i forget something?

thanks in advance

Anonymous said...

have found the problem, it's my mistake, also use setOutputFileName at the same time

Anonymous said...

Hi

I'm using the CSS feature as well in our projects.
What about including other CSS files into the CSS file used within BIRT? Is this supported?

Jason Weathersby said...

BIRT can use CSS style sheets that are imported into the report or if you are using the 2.2 branch you can use style sheets that are located in the resource folder by using the "use sytle sheet" option within the report. This is located in the same place as the import.

Jason

Anonymous said...

CSS supports the "import" of other CSS files.
If I try to use such a CSS file, BIRT recognise only the styles defined in the first CSS file.
Example: a.css defines some style and "import" the b.css.
In b.css there are different styles defined. If I do "use style sheet" with the a.css, I do not see the styles defiened in b.css within my report! Is this not supported in BIRT or is this a bug?

Jason Weathersby said...

This sounds like a bug. Can you please log a bugzilla entry?

Anonymous said...

some onmouse event will be good.

Narendar said...

Hi,

We are using Tomcat 5.5.23 and BIRT2.2.0 version.

I have linked style sheet which is places under resource folder. When we execute the report it is taking the style sheet and displaying the report properly but, when I open parameter page and click on ok, it is not taking the styles linked.
This is working fine when we completely reload the report and not just changing the parameters in opened report.

Can any one help me to resolve this issue?

Thanks in advance.

Anonymous said...

Is it possible, to use a css or a style for a chart or do I have to do this with "setCustomXML" functionality?

Thx in forward

Ben

keith_c06 said...

Is it possible to have a separate style used for different media types (i.e. screen v. print)?
I am able to make my reports look nice on screen, very readable, with colors and nice fonts, but when printing my reports, the fonts are too large to nicely fit each row of data into one line without making all reports print landscaped.

Any insight would be appreciated.

Thanks,

Keith

Jason Weathersby said...

Keith,

This is possible. Create two styles in the report. One for pdf and one for everything else and then use a script like the following in the beforeFactory:
(Notice you will need to name your elements)

tableHandle = reportContext.getDesignHandle().findElement("mytable");

rowHandle = tableHandle.getDetail().get(0);


reportContext.getOutputFormat();

if( reportContext.getOutputFormat() == "pdf" ){

rowHandle.setStyleName("style2");

}else{

rowHandle.setStyleName("style1");

}

keith_c06 said...

Jason,

Your example works in that case, but I was unclear in my previous question. I am using the "WebViewerExample" to quickly integrate the reports for my end users. They will be viewing the reports via a web browser, but will likely want to print some of the reports locally for later use. The reports look nice on screen, but when they print from the web viewer, the report becomes very hard to read.
Is there a way to specify a style just for printing when a report is in HTML format?

Thanks,

Keith

Jason Weathersby said...

Keith,

Are they printing with browser print button or the print icon in the viewer toolbar. If they are using the icon it creates a pdf, so you should be able to change the style in the onRender event instead of the onCreate event.

Jason

keith_c06 said...

Jason,

They are using the print icon in the viewer.

Whenever I move that code snippet to set the style out of the "beforeFactory" event, it no longer sets the style of my data table. What am I missing?

Thanks,

Keith

Jason Weathersby said...

Keith,

Try removing the code from the beforeFactory, select the table row and edit its onRender event with something like this:

if( reportContext.getOutputFormat() == "pdf" )
{
this.getStyle().backgroundColor = "red";
}


Jason

keith_c06 said...

Jason,

Thanks for the tip, that works. However, from my tests so far, it does mean hard coding the style values into the "onRender" event of each element.
To avoid hard coding my style values, I've tried creating a simple java class to set style values for a passed element. The Java class is very simple and has a method "format" that looks like:
public void format(IReportElementInstance element, String elementType) {
element.getStyle().setFontFamily("Monospace");
element.getStyle().setFontSize("6.7pt");
}

I have called this method from the "onRender" event, but with no success (or error messages).

I've tried writing a simple JavaScript that did something similar, but I could not get the report viewer to recognize my JavaScript file (I put it into the "scriptlib" directory).

Are there any other available methods for setting style values in the event handers?

Thanks,

Keith

Jason Weathersby said...

Keith,

If you implemented something in a js file you should be able to include this js file by selecting the report->properties->resources and click the add button under javascript files. This js file needs to be in your resource folder.

If you stick with the java approach, jar your class and put it in the web-inf/lib directory. How did you call the class?

keith_c06 said...

Jason,

In the initialize event of the report:
printformatter = Packages.com.familyvideo.reportutils.VMCRPrintFormat();

My thought was this should create a global variable that I can use throughout the other events (similar to a scripted data source that I've used in another report).
Then in the onRender event of the table row I have this code:
if(reportContext.getOutputFormat() == "pdf") {
printformatter.format(this, "datarow");
}

This seemed like a logical solution to me, but I have not been able to get it to work. I would like to stay with Java as I can package it with other Java utility classes I have used for other reports.

Thanks,

Keith

Jason Weathersby said...

Keith,

I am not certain how you are running the report (2 task or 1 task) but using the global variable approach is ok if you set it like:

printformatter = Packages.com.familyvideo.reportutils.VMCRPrintFormat();
reportContext.setPersistentGlobalVariable( "myfunction", printformatter);

Then when you want to use it:

reportContext.getPersistentGlobablVariable( "myfunction").format();

keith_c06 said...

Jason,

When trying to use the setPersistentGlobalVariable method, I get the error:
There are errors evaluating script "printformatter = Packages.com.familyvideo.reportutils.VMCRPrintFormat(reportContext.getHttpServletRequest()); reportContext.setPersistentGlobalVariable("PrintFormatter", printformatter);": Can't find method org.eclipse.birt.report.engine.script.internal.ReportContextImpl.setPersistentGlobalVariable(string,com.familyvideo.reportutils.VMCRPrintFormat).

So, my class is not recognized by this method.

I tried to use the method you described to attach the JavaScript file to the report, but I still go an error:
org.eclipse.birt.report.engine.api.EngineException: Unable to load Script file print_format.js.

BTW, I'm using the frameset pattern to run the report (which is the 2 task method if I understand correctly).

Thanks,

Keith

Jason Weathersby said...

Keith

1 - In the java scenario where are you getting the error. In what script. BTW move the code to the beforeFactory. It only gets called once. initialize gets called twice with /frameset.

2 - In js scenario where did you put the js file?

keith_c06 said...

Jason,

1 - The error comes from whichever event the printformatter = Packages.com.familyvideo.reportutils.VMCRPrintFormat("report_missing.rptdesign"); reportContext.setPersistentGlobalVariable( "PrintFormatter", printformatter);" lines are in. The error is persistent across all methods of running the report (preview, web viewer from the designer, and deployed web viewer).

2 - I was able to get the js file put in the right place to get it to work, but using javascript for this solution makes this less robust and more difficult for the other developers in my group to support in the future.

Jason Weathersby said...

If you call
printformatter = Packages.com.familyvideo.reportutils.VMCRPrintFormat("report_missing.rptdesign");
only when you need it does it work? I am wondering if it ever finds your class?

keith_c06 said...

Jason,

I tried moving the initialization of my class as you suggested an started watching the log files for issues and found why it is failing (and found a possible solution).
Here is the error from the log:
Cannot convert [object Object] to org.eclipse.birt.report.engine.api.script.instance.IReportElementInstance (/report/body/table[@id="31"]/method[@name="onRender"]#3).
As it turns out, the object passed as this from the onRender event is a org.mozilla.javascript.NativeObject . So, I can pass the StyleInstance object instead. That works...
Also, I've moved the initialization of the class into the beforeRender event for the report (without using the reportContext.setPersistentGlobalVariable method) and it is working like I had hoped.

As always, you have been very helpful in figuring out how to solve the problem!
Thanks,

Keith

Jason Weathersby said...

Keith,

Glad to hear. I forgot that setPersistentGlobalVariable requires that the object be serializable.

Jason

keith_c06 said...

I was able to get the above to work in the designer, but when I deploy it to a tomcat instance (apache version 5.5.27), I come up with errors.
In order to pass the StyleInstance object, I have to use an import statement in my java code:

import org.eclipse.birt.report.engine.script.internal.instance.StyleInstance;

I've done this, and made sure to add the org.eclipse.birt.report.engine_2.5.0.v20081113.jar file to the build path. This works great in the designer, but I get this error in from the apache server:
WARNING: There are errors evaluating script "
printformatter = Packages.com.familyvideo.reportutils.VMCRPrintFormat("report_missing.rptdesign");
":
org/eclipse/birt/report/engine/script/internal/instance/StyleInstance.
org.eclipse.birt.core.exception.CoreException: There are errors evaluating script "
printformatter = Packages.com.familyvideo.reportutils.VMCRPrintFormat("report_missing.rptdesign");
":
org/eclipse/birt/report/engine/script/internal/instance/StyleInstance.
at org.eclipse.birt.core.script.ScriptContext.eval(ScriptContext.java:303)
at org.eclipse.birt.core.script.ScriptContext.eval(ScriptContext.java:331)
at org.eclipse.birt.report.engine.executor.ExecutionContext.evaluate(ExecutionContext.java:653)
at org.eclipse.birt.report.engine.script.internal.ScriptExecutor.handleJSInternal(ScriptExecutor.java:60)
at org.eclipse.birt.report.engine.script.internal.ScriptExecutor.handleJS(ScriptExecutor.java:47)
at org.eclipse.birt.report.engine.script.internal.ReportScriptExecutor.handleBeforeRender(ReportScriptExecutor.java:115)
at org.eclipse.birt.report.engine.api.impl.EngineTask.startRender(EngineTask.java:1355)
at org.eclipse.birt.report.engine.api.impl.RenderTask$PageRangeRender.render(RenderTask.java:509)
at org.eclipse.birt.report.engine.api.impl.RenderTask.render(RenderTask.java:208)
at org.eclipse.birt.report.service.ReportEngineService.renderReport(ReportEngineService.java:1365)
at org.eclipse.birt.report.service.BirtViewerReportService.getPage(BirtViewerReportService.java:202)
at org.eclipse.birt.report.service.actionhandler.AbstractGetPageActionHandler.doExecution(AbstractGetPageActionHandler.java:240)
at org.eclipse.birt.report.service.actionhandler.AbstractGetPageActionHandler.__execute(AbstractGetPageActionHandler.java:107)
... cut down for space.

The jar that I am attaching in the build path is one of BIRT's major pieces, so does anyone have any ideas as to why I can't get tomcat to let me use it in my java class?

Thanks,

Keith

Jason Weathersby said...

Keith,

You did not put this
org.eclipse.birt.report.engine_2.5.0.v20081113.jar
in the cp for tomcat did you? This is the plugin jar and I would expect it to be available to the event handler without having to do this.

keith_c06 said...

Jason,

I have tried it with and without that jar in the classpath, and it fails both ways.
Maybe this isn't even a BIRT issue, it may be a tomcat issue. Here's my thinking: the jar would be available if it were called directly through the event in the report, but it is called through my custom Java code, and tomcat does not know where to find this jar when it is called in my java code. I had a similar problem with the mysql driver and had to copy the jar in the the apache/common/libs directory to get my scripted data source working in deployment. I tried doing the same with this jar and tomcat started with errors and would only display an error report in my browser when I tried to go to http://localhost:8080/WebViewerExample.

Jason Weathersby said...

Keith,

Any chance you could email me your event handler (remove client specific logic) let me try on my tomcat instance? If so email it to jasonweathersby at alltel.net.

Jason

Anonymous said...

toc-level-0[-9] does not appear to have any effect. There are some TOC items on my report I'd really like to style. can I get to these elements with imported css file?

Diego said...

hi, i followed the steps and it doesn't work.

I have a report with a table and want to colour it's rows, based on a css file.

I clicked on "Use CSS", then selected it, got all classes linked.

When i run the report, no visible changes, and styles do not apply to the table, ( i put .table{background-color:gray;}).

I checked the html and no css import on the head.

Then i go to the design, and on the table properties editor/General/Style, and select table.

On the layout, on eclipse, i see the colors, but nothing happens when the report is executed.


Hope you can help.
Thanks in advance,
Diego

Scott Rosenbaum said...

Diego,

What version of BIRT are you using?

sr

Diego said...

I am using 2.5.1. When i "Use CSS" files, i can see from the generated HTML that the classes in my css file are imported into the html, but i have another problem.

I have a table, and applied a style, which i Use from my css file. This table isnt taking theese styles because on the generated html it is:


table id="AUTOGENBOOKMARK_2" class="style_2" style="border-collapse: collapse; empty-cells: show; width: 100%; table-layout: fixed; text-align: center;" ...

So it will never take the styles defined in .table (because it's class is "style_2"

I do not really know how to fix this, it seems to be something related to the generation or table config

Thanks in advance

Scott Rosenbaum said...

Diego,

If you send the .CSS file and the .rptdesign to scottr (at) innoventsolutions (dot) com and I will take a look.

Also if you can let me know which table is causing the problem.

Shishir said...

Is there a way, where I can change the default border-collapse:collapse property from collapse to separate. I am unable to get this working.

Anonymous said...

Hi,
How can I apply css style only to one element?
For example I have element "label" with name="Title" and id="1"

I tried:
Title{}
#1{}
but both don't work.

Only
.label{}
works, but this rule is apply to all labels. And, again, I want to apply rule only to one element.

Thanks for any tips.

Scott Rosenbaum said...

If you want to apply a style to only one label, you want to use a custom style, call it MyLabel.

Create the style and then go to the label in the property editor. The property editor will have a drop down called style. You will be able to select MyLabel from the drop down list.

NOTE: Pre-Defined styles do not show up in the drop-down. (e.g. label is pre-defined name so it won't show in the drop down).

ritika said...

Hi is there way we can have fixed report/table header in BIRT like in excel

Jason Weathersby said...

Take a look at:
http://www.birt-exchange.org/org/devshare/designing-birt-reports/795-add-a-table-with-a-fixed-header-row/

ritika said...

This sample seems not be working in current version 11sp3. The table header is not fixed. Please share if you have any latest copy.thanks

Jason Weathersby said...

Are you viewing it just in html? Not in the webviewer? Change the page break interval for the second table from 50 to 0 and then view in html.

Jason

ritika said...

Yes I tried set the second table page break value to 0, and view the report in HTML from Designer ,only I can see the Header.Please correct me if I am missing something.Thanks

Jason Weathersby said...

That worked for me. What do you mean by the second page break?

ritika said...

As you mentioned Change the page break interval for the second table from 50 to 0 and then view in html. the report has two tables I changed page break interval from 50 to 0 for second table.

Anonymous said...

Hi i have created a master page for my birt reports in master page i have added a new theme where added a predefined style as table-header-cell where i set a background image for the table header.
This style is working fine once i export my report to PDF or HTML fornmat ,but if i export same report in DOCX format the background iamge is not coming up. Please help i laready waisted 3 days in finding the solution