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:

  1. Anonymous8:12 AM

    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

    ReplyDelete
  2. Anonymous12:25 PM

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

    ReplyDelete
  3. Anonymous2:15 PM

    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?

    ReplyDelete
  4. 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

    ReplyDelete
  5. Anonymous4:41 AM

    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?

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

    ReplyDelete
  7. Anonymous10:40 AM

    some onmouse event will be good.

    ReplyDelete
  8. 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.

    ReplyDelete
  9. Anonymous4:27 AM

    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

    ReplyDelete
  10. Anonymous8:28 AM

    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

    ReplyDelete
  11. 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");

    }

    ReplyDelete
  12. Anonymous1:34 PM

    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

    ReplyDelete
  13. 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

    ReplyDelete
  14. Anonymous7:42 AM

    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

    ReplyDelete
  15. 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

    ReplyDelete
  16. Anonymous9:48 AM

    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

    ReplyDelete
  17. 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?

    ReplyDelete
  18. Anonymous12:27 PM

    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

    ReplyDelete
  19. 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();

    ReplyDelete
  20. Anonymous1:11 PM

    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

    ReplyDelete
  21. 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?

    ReplyDelete
  22. Anonymous2:55 PM

    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.

    ReplyDelete
  23. 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?

    ReplyDelete
  24. Anonymous9:16 AM

    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

    ReplyDelete
  25. Keith,

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

    Jason

    ReplyDelete
  26. Anonymous12:11 PM

    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

    ReplyDelete
  27. 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.

    ReplyDelete
  28. Anonymous1:03 PM

    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.

    ReplyDelete
  29. 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

    ReplyDelete
  30. Anonymous8:44 PM

    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?

    ReplyDelete
  31. 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

    ReplyDelete
  32. Anonymous10:01 AM

    Diego,

    What version of BIRT are you using?

    sr

    ReplyDelete
  33. 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

    ReplyDelete
  34. Anonymous10:02 AM

    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.

    ReplyDelete
  35. 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.

    ReplyDelete
  36. Anonymous7:29 AM

    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.

    ReplyDelete
  37. Anonymous7:48 AM

    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).

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

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

    ReplyDelete
  40. 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

    ReplyDelete
  41. 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

    ReplyDelete
  42. 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

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

    ReplyDelete
  44. 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.

    ReplyDelete
  45. Anonymous2:27 AM

    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

    ReplyDelete