PDF From A Zen Report

21 views
Skip to first unread message

APZ

unread,
Mar 5, 2012, 3:12:08 PM3/5/12
to InterSystems: MV Community
This is the 2nd of the Zen Reports-related posts in the Caché of Tips
series. Zen Reports provides the ability to define the data and layout
of a report. Using a Zen Report, you can create a PDF file, a web
page, or even an Excel spreadsheet. The Zen Report feature is included
in Caché and can be used as replacement to other report generators
such as Crystal Reports.


In the first Zen Reports Tip, we created the "ReportDefinition" block
of a Zen Report class. This is the part which collects (and
aggregates) your data from the database and structures it as XML.

In this Tip, we move on to the "ReportDisplay" block, where the layout
is controlled. We will set up a fairly simplistic PDF. Even so, this
will be a lot of information to cover for one Tip. I recommend that
you download the class MyApp.ZenRpt.Attorney2PDF from
http://www.google.com/url?sa=D&q=https://sites.google.com/site/intersystemsmv/home/a-cache-of-tips/pdffromazenreport
and have it open while reading this Tip.



CONFIGURATION

In versions of Caché prior to 2010.2, the system requires some
configuration in order for Zen Reports to generate PDF output. You
must install a rendering engine (examples include Apache FOP or
RenderX XEP), and you must configure a system setting with the path to
that external rendering engine. How to do this is documented in
http://docs.intersystems.com/cache20121/csp/docbook/DocBook.UI.Page.cls?KEY=GRPT_report_running#GRPT_not_builtin_FOP

In versions later than 2010.2, there is no system-level configuration
needed for generating PDF files.

To generate a PDF from a Zen Report, you set up the layout you want
using ReportDisplay, and then set the class parameter DEFAULTMODE to
"pdf". To quickly run the report, click View > Web Page, from the
Studio menu.


DESIGN

Before we look at the ReportDisplay, let's decide on a rough idea of
what output we want. The idea is that we are making a "Performance
Review" report for a law firm We are using data from the ATTORNEY file
to create the Attorney Performance Review report. The key factor for
the review is each Attorney's average Percent Varience -- a value of
100 means they are billing exactly the ideal number of hours. Higher
means overbilling, lower means underbilling.

Let's say we want a cover page that gives a summary of all the
Attorneys' results. Then, one page for each Attorney with their
details. Each attorney's page should have his/her name and all his/her
data, plus a chart showing his/her percent variance score over time.

As you build a Zen Report, keep in mind the PDF output usually works
best if you specify dimensions (specifically width) for the
components. You may find it easiest lay out the items in their general
positions first, then go back and adjust sizes until things look just
right.



STRUCTURE

In the ReportDefinition, we used a limited set of "building blocks" to
control the structure & content of XML which serves as an intermediate
format. In the ReportDisplay, we arrange & lay out content plucked
from the intermediate XML.

The XML output from the report's ReportDefinition looks like :
<myReport >
<Attorney Name="Burstin, Lee">
<Period Period="201202">...<PercentVarience>100</
PercentVarience></Period>
<Period Period="201203">...<PercentVarience>106</
PercentVarience></Period>
...
<Attorney_AvgPctVar>103</Attorney_AvgPctVar>
</Attorney >
<Attorney Name="Zonderman, Ariela">
<Period Period="201202">...<PercentVarience>90</
PercentVarience></Period>
<Period Period="201203">...<PercentVarience>76</
PercentVarience></Period>
...
<Attorney_AvgPctVar>83</Attorney_AvgPctVar>
<Attorney >
...
</myReport>


The elements are named "myReport", "Attorney", "Period",
"PercentVarience", and "Attorney_AvgPctVar". Some of those were built
by the <aggregate> building-block, some by the <group> building-block,
and some were built by the <element> building-block, back in
ReportDefinition. But by the time ReportDisplay is dealing with them,
they are all treated equally as elements.

The attributes in this example are "Name", an attribute of <Attorney>;
and "Period", an attribute of the element also named "Period". To
refer to them, we would write "@Name" or "@Period".

Zen Reports uses the term "field" to refer to values from both
attributes and/or elements.

As you pluck values out of the XML to use in the display, you need to
be careful about these few things :
1. to refer to the value of an element, use its name (the value is
the content between the <tag></tag>).
2. to refer to the value of an attribute, you need to prefix the
name with the @-sign.
3. the names are case-sensitive.
4. the names are context-sensitive.

Context-sensitivity means that you can't use the field names in
isolation. You need to "focus" on the <Attorney> element before you
can refer to the @Name field. Similarly, if you have drilled down into
the <Period> element, you can't reference the field @Name because it's
not an attribute of the current element. I'll get into "context" more,
later.

One extra thing that it is helpful to know is that you can chain names
together to drill down into the structured content. The separator-
character when doing this is / (forward slash). For example :
"Attorney/Period/PercentVarience", or "Attorney/@Name".

Internally, the Zen Report is building and using XPath. So, if it
happens that you are familiar with XPath, you can apply predicates or
functions to those names. For those who have not studied XPath, just
keep the above 4 points in mind and the Zen Report "plumbing" takes
care of the rest!



LAYOUT

The set of building-blocks available to us in ReportDisplay is
extensive. In this Tip, we will stick with the following very basic
set:
<group name="" >
-- used to loop over repeating values, no output of its
own. Affects "context" for field-names.

<table group="" orient="" >
-- used for layout (yes, old-school), and to loop like a
group

<item field=""><caption value="" /><summary value="" /></item>
-- used for displaying a value, either a literal value or
a value from the data

<div >
-- used for applying width or other styles to several
building-blocks at once

<pagebreak>
-- makes a page break (only useful in PDF).

Plus, as a bonus, a <barChart>, which I won't explain in detail in
this Tip but which is represented in the attached
MyApp.ZenRpt.Attorney2PDF class. As suggested by the name, this
building block is used to display a bar chart.



The <item> building-block is used to echo text content into the
output. The <item> has attributes which we use in order to select the
content :

use "field" to get a value (identified by name) from the data
use "value" to type in a literal string
use "expression" to type in COS expression which evaluates to
a string (can't use MV)
use "special" to calculate a page-number, or table-row number

For example, to output the Attorney name, we would enter :
<item field="@Name" />
(assuming that the context for the <item> is at the Attorney --- see
<group> for details).

The simplest way to concatenate values is to use one <item> after
another. All consecutive <items> will appear on the same row (unless
they are in a <table>). To keep some <item>s together on one line,
and make a break between lines, you can wrap the set of <item>s in a
<div>. Example:
<div> <item field="@Name" /> <item value=", ESQ." /> </div>



The <group> building-block establishes the "context" for all building-
blocks contained within it. To do this, specify the name of the XML
element in the "name" attribute of the <group>. If there is more than
one element of that name, the content of the <group> will be repeated
for each one. Thus, the <group> is used to loop over repeating data
from the Report Definition. (The "name" is technically an XPath
expression, so if you wanted to drill down thru a couple of nested
elements at once, you could do so by combining their names with "/".)

For example, to output all of the names of the attorneys, one per
line:

<!-- first page -->
<group name="Attorney">
<div> <item field="@Name" /> <item value=", ESQ." /> </div>
</group>

If there are 8 Attorneys, the <group> loops 8 times, and processes the
one <item> each time.

We'll use this listing as the front page of the report. It's not very
pretty, but this is just an example. To end the page, enter
<pagebreak />.

There's a shorthand for adding a pagebreak at the end of each of the
<group>'s loops : set the attribute pagebreak="true" on the <group>.
This is how we make sure each Attorney's details starts on a new page.

<!-- rest of the pages -->
<group name="Attorney" pagebreak="true" >





The <table> is a work-horse tag. Like the <group>, a <table> can
change the field "context" for building blocks within the <table>. It
is also a layout control.

Whever you use a table in PDF, the default is for the table to extend
the full width of the page and for all of the columns to be the
equally-wide. If you don't want your columns to take up the same
width, set the "width" attribute on each of the other building blocks
that are inside the <table>.

A Zen Report <table> can contain more than just <item>s. It can
contain *almost* any other ReportDisplay building block. And anything
that can be in a table can have a "caption". The caption is what's
displayed in the column-header.

Example:
<item field="@Name" caption="Attorney Name"/>

It's equally permitted to specify the caption by using a <caption>
child inside the <item> tags. Example:
<item field="@Period"><caption value="Period" /></item>

Use the <caption>-child technique if you want to pull a value from the
data to use as the caption.
<item field="Attorney_AvgPctVar"><caption field="@Name" /></
item>

Similar to the <caption> tag, there is a <summary> tag which can be
used to add a footer-cell below the the column.

If the <item> (or other building block) is not inside a <table>, the
caption will not show because there's no column-header position to
place it into. The summary also won't show outside a table. If you
really want a label regardless, there's some hope : Fill in the
<item>'s caption attribute and also set the "displayCaption" attribute
to true. This will concatenate the caption onto the value. You can use
this trick even within a <table>. See an example:

<summary field="Attorney_AvgPctVar" caption="Avg : "
displayCaption="true" />

The table's "orient" attribute determines whether the building-blocks
it contains are laid out side-by-side with the caption on top
(orient="col") or top-to-bottom with the caption on the left
(orient="row").



The <table> building-block can also loop over repeating data, just
like a <group>. To do this, specify the name of the repeating XML
element in the "group" attribute of the <table>. When you set the
"group" attribute, this changes the context for the building blocks
contained within the <table>.

Example:

<table group="Period" >
<item field="@Period"><caption value="Period" /></item>
</table>

The table can even sort the data it is looping over. Do this by
setting "orderby". For instance, our report's Query sorted the data
chronologically, oldest to newest. We can re-arrange it for display so
that the newest periods are at the top:

<table group="Period" orderby="@Period\DESC" >

If you make use of orderby, be careful. The values you are sorting on
will be sorted alphabetically, not numerically. This means "100" will
come before "20". To avoid this, use the function "number()" around
the name (but not around the "\ASC" or "\DESC"). There's an example on
the first page, with a <group> :

<group name="Attorney" orderby="number(Attorney_AvgPctVar)\DESC">

For a "col"-oriented table that loops over repeating data, you get a
new row each time the table loops. For a "row"-oriented table, where
each building-block lays out its content on its own row, each loop
results in a new column.





Other than the <item>, which shares its line with other <item>s, and
the <group>, which doesn't directly result in any output on its own,
most of the other building-blocks one can use in a Zen Report use up a
full line to themselves. They are referred to as "block-level
content".

But sometimes you want to force block-level items to share a line. The
simplest way to arrange two blocks of content side-by-side is to put
the building-blocks within a table with orient="col" and no "group"
defined. For example, we want to place the barchart on the right of
the table of name & date info that starts off the page for each
Attorney. To do this, we wrap those two building blocks inside an
extra table:

<table orient="col">
<table orient="row"> ... </table>
<barChart >... </barChart>
</table>





Finally, let's change the page-size. This is only relevant for PDF
output, of course. Keeping in mind that we're playing with Attorney
data, we'll make the report use legal-sized pages (8.5 × 14 inches).

To adjust the page-size, find the <document> element in the
ReportDisplay. This is provided automatically when you create a Zen
Report class using the Studio "New..." menu. The "height" attribute
was 11 inches by default; we change that to 14 inches :

<document width="8.5in" height="14in" ... />



CONCLUSION

All of the techniques I mentioned can be seen in use in the class
MyApp.ZenRpt.Attorney2PDF

As mentioned above, to quickly run the report from Studio, click View
> Web Page, from the Studio menu. The report can also be run simply by
typing its URL into a web-browser in the form
http://server:port/csp/namespace/package.class.cls



One final word : TROUBLESHOOTING.

The very first thing to check is whether the Report can generate XML
output. To try this, either change the class parameter DEFAULTMODE to
"xml" and re-compile the report before re-running it; or run the
report as-is, then add the URI query-string parameter $MODE=xml in the
browser, and re-load the page.

The second thing to check on, especially if your PDF is coming up
blank, is the rendering log. To do this, re-run the report with the
added URI query-string parameter $LOG=1.

Most of the time, the log will give enough information to help pin
down what's going wrong. Often, it's as simple as correcting the name
of a field --- @ for attributes, plain name for elements, all case-
sensitive --- or correcting a non-unique "id" value.

If you need to do more troubleshooting than this, check the
documentation's Troubleshooting section.



Ariela Zonderman
SQA Engineer
InterSystems Corp

Dawn Wolthuis

unread,
Mar 5, 2012, 3:25:19 PM3/5/12
to intersy...@googlegroups.com
Awesome Ariela! We have a very simple Zen report that we would like to
turn into a pdf, although there are plenty of things on the plate
before we get to this. It is great to know that there is no sys admin
required on 2010. I will be re-reading this with more attention to
detail at some point. It is much appreciated. --dawn

> --
> You received this message because you are subscribed to the Google Groups "InterSystems:  MV Community" group.
> To post to this group, send email to Cac...@googlegroups.com
> To unsubscribe from this group, send email to CacheMV-u...@googlegroups.com
> For more options, visit this group at http://groups.google.com/group/CacheMV?hl=en

--
Dawn M. Wolthuis

Take and give some delight today

Reply all
Reply to author
Forward
0 new messages