6
Refining a Document
Headers and Footers Multiple Columns Page Numbers Creating Macros
Units of Measurement Importing Images Displaying Imported Components Creating Draw Styles
Drawing Shapes Render Objects Listening for JClass PageLayout Events
6.1 Headers and Footers
You create headers and footers in the page template by defining frames not connected to the document's main flow. Building Page Templates, in Chapter 2, shows you how to use JClass PageLayout's default templates, and lists the XML elements you can use to write your own templates.
Because header and footer frames are not connected to the main flow of the document, but are defined separately, any text or images you render to those frames in a page template are replicated on every page that is based on that template. For an example, refer to Section 6.3, Page Numbers.
The following XML template lays out a standard 8.5x11 page consisting of a header frame, a body frame, and a footer frame.
<PAGE NAME="BookLeft" UNIT="inches">
<LOCATION X="0" Y="0"/>
<SIZE WIDTH="8.5" HEIGHT="11.0"/>
<FRAME NAME="header">
<LOCATION X="0.5" Y="0.5"/>
<SIZE WIDTH="7.5" HEIGHT="0.5"/>
</FRAME>
<FRAME NAME="body">
<LOCATION X="0.5" Y="1.25"/>
<SIZE WIDTH="7.5" HEIGHT="8.5"/>
</FRAME>
<FRAME NAME="footer">
<LOCATION X="0.5" Y="10"/>
<SIZE WIDTH="7.5" HEIGHT="0.5"/>
</FRAME>
<FLOWFRAME NAME="body"/>
<FLOWPAGE NAME="BookRight"/>
<FLOWSECTION NAME="BookChapter"/>
</PAGE>This page template defines the following layout.
Figure 21 : Header, Body, and Footer frames.
6.2 Multiple Columns
To create multiple columns, add
<FRAME NAME="body">COLUMN COUNT
andSPACING
attributes to the definition of the body frame in the XML template, for example:
<LOCATION X="0.5" Y="1.25"/>
<SIZE WIDTH="7.5" HEIGHT="8.5"/>
<COLUMN COUNT="2" SPACING="0.5"/>The
COLUMN COUNT
andSPACING
parameters instruct JClass PageLayout to flow text through the body frame in two columns, separated by a gap of 0.5". Columns are always of equal width.The changes to the template produce the following results.
Figure 22 : Text flowing through columns.
6.3 Page Numbers
You number pages by embedding a macro in the frame that is to contain the page number, usually the header or the footer.
JCPage template_page = doc.stringToTemplate("bookLeft");
JCFrame footer_frame = template_page.stringToFrame(FOOTER);
JCTextStyle style = JCTextStyle.stringToStyle("default text");
try {
footer_frame.print(style, TextMacro.PAGE_NUMBER);
}
catch (EndOfFrameException e) {}The preceding example uses the
stringToFrame
method to establish that thefooter_frame
is to hold thePAGE_NUMBER
macro. Once the macro is embedded in the page template, it is added to the footer frame and evaluated whenever a new page is generated from this template.Any content added to template pages should be done before any template pages are used, that is, before
JCFlow
is instantiated (otherwise content may not appear on the first page of the document). Please see Building Page Templates, in Chapter 2, for more information.You must repeat this process for every page template used to generate document pages that contain page numbers.
template_page = doc.stringToTemplate("bookRight");
footer_frame = template_page.stringToFrame(FOOTER);
// print the page number macro to the frame
try {
footer_frame.print(style, TextMacro.PAGE_NUMBER);
}
catch (EndOfFrameException e) {}In this manner, you have precise control over which pages display page numbers and which do not.
The
TextMacro
interface gives you the following options:
6.4 Creating Macros
In addition to the predefined macros in
TextMacro
(see Section 6.3, Page Numbers), JClass PageLayout allows you to create customized macros that allow the insertion of custom run-time text into a document.To create your own macro, you must first create a java class that implements the
TextMacro
interface. (TextMacro
is in thecom.klg.
jclass.page
package.)TextMacro
specifies three methods that must be implemented in your macro class:evaluate()
,getStatus()
, andgetText()
. Once the macro has been added to a document, each implemented method will be called by JClass PageLayout.If
getStatus()
andgetText()
are called beforeevaluate()
, you must ensure that they returnMACRO_INITIALIZED
and a non-null placeholder Sting, respectively. Onceevaluate()
is called by JClass PageLayout, the method should attempt to construct the text String that is represented by the macro.If the macro can be evaluated given the current flow and page information,
evaluate()
must returnMACRO_EVALUATED
, as must any subsequent calls togetStatus()
. Subsequent calls togetText()
must return the evaluated text String.If the macro cannot be evaluated given the current flow and page information,
evaluate()
must returnMACRO_NOT_YET_EVALUATED
, as must any subsequent calls togetStatus()
. Subsequent calls togetText()
must return a non-null placeholder String.Note:
evaluate()
may be called by JClass PageLayout several times per macro (and may be passed different, potentially null values for the flow and page parameters). Onceevaluate()
returnsMACRO_EVALUATED
for the instance of the macro within the current frame, it is never called again. If the macro is being used in a static frame across multiple pages (for example, in the header of a table or a static page frame), theevaluate()
method will be called again when any new frame containing the macro instance is created by JClass PageLayout.For example, here is a class that overrides
import com.klg.jclass.page.*;TextMacro
and printscontinued
each time it is evaluated, except the first time.
public class ContinuedMacro implements TextMacro {
/** The text to which this macro has been evaluated */
protected String text = "";
/** The status of the result of the last evaluation */
protected int status = TextMacro.MACRO_INITIALIZED;
/** True the first time this macro is evaluated; false otherwise. */
private boolean firstTime = true;
public ContinuedMacro() {
}
/** Return currently evaluated text. */
public String getText() {
// if there is no current value for the macro, return placeholder text
if (text == null) {
return ("");
}
return (text);
}
/** Return current evaluation status. */
public int getStatus() {
return (status);
}
/** Evaluate macro. Parameters flow and page may be null. */
public int evaluate(JCFlow flow, JCPage page) {
// if flow or page is null, don't evaluate
if (flow == null || page == null) {
text = null;
status = TextMacro.MACRO_NOT_YET_EVALUATED;
} else {
// first evaluation -- text is blank
if (firstTime) {
text = "";
firstTime = false;
status = TextMacro.MACRO_EVALUATED;
// all other evaluations -- text is "continued"
} else {
text = "continued";
status = TextMacro.MACRO_EVALUATED;
}
}
// return evaluation status
return status;
}
}Note: This macro can be used in many instances; for example, in table headers.
To add this macro to a frame, use:
try {
frame.print(textStyle, new ContinuedMacro());
} catch (EndOfFrameException eofe) {
}
To add this macro to the flow, use:
flow.print(new ContinuedMacro());
6.5 Units of Measurement
Many JClass PageLayout functions require you to measure distances on the output page. For example, to import images or draw shapes, you must pinpoint the location on the page where the image or drawn object is to be placed. To do so, you must possess an understanding of the methods JClass PageLayout provides for the precise measurement of linear distances.
The
JCUnit
class lets you define linear distances using three different units of measurement: centimeters, inches, and points. You can set default units and convert distances from one unit type to the next. You can useJCUnit.Point
to precisely identify a location on a page andJCUnit.Margins
to create a margin on the inside of a frame.
6.5.1 Setting a Default Unit of Measurement
You can set the default unit type to be centimeters, inches, or points. Once you set a default unit, all methods that use measurement units use the default, unless instructed otherwise. For example, to set centimeters as the default unit type, enter:
JCUnit.setDefaultUnit(JCUnit.CM);
6.5.2 Converting Units of Measurement
Your application may need to convert a distance from one unit type to another on the fly.
JCUnit.Measure measurement = new JCUnit.Measure(JCUnit.CM, 5);JCUnit
provides methods to convert a distance to each supported unit type. For example, suppose you have defined a distance in centimeters, as follows:To convert
double distanceInInches = JCUnit.getAsInches(distance
to a measurement in inches, enter:
measurement.units, measurement.distance);
measurement.getAs(JCUnit.INCHES);
6.5.3 Defining Points
Some JClass PageLayout functions require you to define a location on a page, for example, in order to draw a line or a polygon.
JCUnit.Point point = new JCUnit.Point(JCUnit.INCHES, 2.5, 2.5);JCUnit.Point
makes it possible for you to precisely define locations, using any of the available units of measurement.The preceding example pinpoints a location on the page at the X- and Y-coordinates of 2.5 inches by 2.5 inches. To draw a line or a polygon, you would define other points on the page and use the corresponding
JCFrame
method to connect those points with lines. For more information, refer to Section 6.9.1, Drawing Lines, and Section 6.9.5, Drawing Polygons.
6.5.4 Creating Margins
You can use
JCUnit.Margins
to set margins around the interior of a frame, for example, inside the body frame on your page.
Figure 23 : Margins inside a frame.
These margins are the same as those created when you define the frames in a page template. For more information, refer to Building Page Templates, in Chapter 2.
This example assumes that the
JCFrame frame = template.stringToFrame("body");JCPage
template is namedtemplate
.
frame.setMargins(new JCUnit.Margins((JCUnit.POINTS, 5, 5, 5, 5));This example creates a margin of 5 points between the edge of the frame and any object rendered to it.
JCUnit.Margins
provides methods that allow you to specify the width of individual margins.
6.6 Importing Images
To add an
Image image = Toolkit.getDefaultToolkit().getImage("image.jpg");Image
to a document, the first step is to load the image file. For example:This example instantiates the image as a
java.awt.Image
object and usesToolkit.getDefaultToolkit().getImage()
to load the image from its file source.Next, you use a
JCFrame
orJCFlow
embed()
,float()
, orpaste()
method to render the image into the current frame or flow. Recall thatJCFrame
methods render content apart from the main flow of the document, while theirJCFlow
counterparts render content into the flow.Embedding places the image on the current line of text, which will wrap if there is not enough space on the current line to hold the image. For this reason, the
embed
method is often used for smaller graphics.Floating an image inserts it on its own line. If there is not enough space on the page to hold the image, it "floats" to a roomier location, for example, the top of the next page. For this reason, the
float
method is often used for larger graphics.Pasting an image locks the image at a set of coordinates you define. The
paste()
method is used to import images that must always appear at the same location, such as a logo in company letterhead. Pasting is only available as aJCFrame
method.In the following example, the image is resized to 50 by 50 points and embedded on the current line.
try {
flow.embedImage(image, new JCUnit.Dimension(JCUnit.POINTS,
50, 50));
}
catch (EndOfFrameException e) {
System.out.println(e.toString());
}The following table describes the types of methods available for importing images.
6.6.1 Importing EPS Images
If your application prints to
EPSImage epsImage = new EPSImage(new BufferedReader(reader));JCPostScriptPrinter
, you can add EPS images to the document. You instantiate the file usingEPSImage
and provide an open reader to the EPS data.Next, you import the image using an
flow.floatEPS(epsImage, new JCUnit.Dimension(JCUnit.POINTS, 75, 100));embedEPS()
,floatEPS()
, orpasteEPS()
method:
6.6.2 Importing Swing Icons
You can also import icons from the
Icon icon = new ImageIcon(image);javax.swing.icon
class into a JClass PageLayout document in much the same manner you import other images.
flow.embedIcon(icon);The preceding example creates a Swing icon based on the image defined earlier, and places it on the current line in the flow.
6.7 Displaying Imported Components
In addition to displaying images defined by image files, JClass PageLayout is capable of importing and displaying a visual object, such as a JClass Chart, so long as the object is of type
java.awt.Component
. The process is simple: instantiate the component and pass it to the current flow as a parameter in the flow'sembedComponent()
method.Note: If you choose to embed a component using
embedComponent()
, the component cannot be changed until after theJCDocument.print()
method has been executed. Because a document is only a series of information and references until it is printed, changing a component before it has been printed may change the output of the component, resulting in it being drawn differently than what was originally intended.An example of a page containing a JClass Chart can be found in ChartExample.java. Once a component that knows how to draw itself is instantiated, one line is all that is needed to embed it in the flow:
// Create new chart instance.
chart = new JCChart();
// Load the chart's data from a data source
// so there is something to display, then embed it:
// ...
flow.embedComponent(chart); // Places the chart in the flow
// The following adds a caption to the embedded image:
flow.newLine();
flow.setCurrentTextStyle(JCTextStyle.ITALIC);
flow.print("Figure 1.1 A Simple Chart");The image of the component may be drawn using:
flow.floatComponent(chart);In this case, the image is drawn after the current line. If the command is encountered while there is a partial line being output, that line will be completed before the image is positioned on the page. The method takes alignment parameters to further control its position.
If you need to separate the image from the current line, bracket the call to
flow.newLine();embedComponent()
with newlines, as follows,
flow.embedComponent(chart);
flow.newLine();Method
embedComponent()
takes ajava.awt.Component
as a required parameter and two optional parameters: alignment and size. The alignment parameter takes one of the alignment constants inJCDrawStyle
for positioning an object vertically relative to the line it is on. This parameter is useful for positioning a small component's image. The size parameter is specified with aJCUnit.Dimension
object and permits scaling the image both horizontally and vertically.Besides setting the size by passing a size parameter to
embedComponent()
, a component's printable size is determined:
- By the component's preferred size, if no other size setting has been made.
- By using the component's
setSize()
orsetPreferredSize()
method, if it has one.- By its instantiated size if the application creates a instance of the component and displays it on-screen in a frame before flowing it on a page, as is done in ChartExample.java.
6.7.1 Native Scaling
You can use JClass PageLayout to print components and images to EPS, PS, PDF, or PCL. Note that you can embed components and images to achieve greater resolution through the use of the output type's native scaling. This process is for only PDF, PS and EPS (AWT printing is constantly at 72 dpi -- this is a Java limitation).
For instance, if you have an image that is 300 pixels by 300 pixels and you want to display it at a resolution of 300 dpi, you should embed the image at a size of 1 inch by 1 inch. Using this same image, if you wanted a resolution of 600 dpi, you would embed the image at a size of 0.5 inches by 0.5 inches.
6.8 Creating Draw Styles
Adding Borders, in Chapter 4 demonstrates the use of draw styles when drawing borders in a table. Later in Section 6.9, Drawing Shapes, you'll define a draw style to use.
Draw styles define the appearance of objects drawn on a page, such as the lines used in tables.
JCDrawStyle
provides methods and procedures for defining draw styles you can use to control the appearance of the lines and fills of the drawn objects in your document.The following example defines a draw style named
JCDrawStyle ds = JCDrawStyle.LINE;ds
.
6.8.1 Setting Line Properties
ds.setLineWidth(new JCUnit.Measure(JCUnit.POINTS, 5));
JCDrawStyle
provides methods you can use to control the appearance of lines drawn in the draw style you've created. For example, to adjust the thickness of the line to 5 pts, enter:To change the solid line to a dashed line, enter:
ds.setLineType(LINE_TYPE_BROKEN);The following table describes the
JCDrawStyle
methods you can use to modify line styles.
6.8.2 Setting Fill Properties
By altering the draw style, you can adjust the fill color of two-dimensional objects, such as circles, rectangles, or polygons.
JCDrawStyle
provides separate methods for the specification of line and fill colors.The following example modifies the draw style created in the previous section (
ds.setFillForegroundColor(yellow);ds
), by setting its fill foreground color to yellow. You can specify any fill color you have defined usingjava.awt.Color
.
6.9 Drawing Shapes
Using JClass PageLayout, your Java application can draw and print a variety of geometric shapes, including lines, circles, rectangles, rounded rectangles, and polygons.
To draw a shape, you must provide a
JCDrawStyle ds = JCDrawStyle.LINE;JCDrawStyle
that describes its appearance. (For more information, refer to Section 6.8, Creating Draw Styles.) In simple cases such as the following examples, you can use a default style, for example:
6.9.1 Drawing Lines
To draw a line, you create an array of points, then call
ArrayList list = new ArrayList();JCFrame.drawLine()
to connect those points with a line drawn in the currentJCDrawStyle
.
list.add(new JCUnit.Point(JCUnit.CM, 2, 3.5));
list.add(new JCUnit.Point(JCUnit.CM, 2, 5.5));
frame.drawLine(ds, list);The preceding example draws a line between the two points defined in the array, as illustrated in the following diagram.
Figure 24 : A drawn line.
6.9.2 Drawing Rectangles
To draw a rectangle, use
frame.drawRectangle(ds, new JCUnit.Point(JCUnit.POINTS, 25, 100),JCFrame.drawRectangle()
. For example:
new JCUnit.Dimension(JCUnit.POINTS, 50, 50));The
drawRectangle()
method creates the outline of a rectangle.JCUnit.Point
places the upper-left corner of the rectangle on the X- and Y-coordinates of 25 by 100 points.JCUnit.Dimension
makes the rectangle a square by setting its size to 50 by 50 points.
Figure 25 : An outlined square (50 by 50).
To draw a filled rectangle, use
frame.fillRectangle(ds, new JCUnit.Point(JCUnit.POINTS, 100, 100),fillRectangle()
. The color used to fill the rectangle is the color defined byJCDrawStyle
.setFillForegroundColor()
.
new JCUnit.Dimension(JCUnit.POINTS, 50, 50));The sample code draws a square with a 100% black fill at the X- and Y-coordinates of 100 by 100 points, with dimensions of 50 by 50 points.
Figure 26 : A filled square (50 by 50).
6.9.3 Drawing Rounded Rectangles
You can also draw outlined and filled rectangles with rounded corners. For rounded rectangles, you need to specify the radius of the rounded corners using
JCUnit.Measure
.To draw the outline of a rounded rectangle:
frame.drawRoundedRectangle(ds, new JCUnit.Point(JCUnit.POINTS,350, 350), new JCUnit.Dimension(JCUnit.POINTS, 50, 50),
new JCUnit.Measure (JCUnit.POINTS, 5));The preceding example produces a rounded rectangle with dimensions of 50 by 50 points and a corner radius of 5 points:
Figure 27 : An outlined square with rounded corners.
To draw a filled, rounded rectangle:
frame.fillRoundedRectangle(ds, new JCUnit.Point(JCUnit.POINTS, 400, 400), new JCUnit.Dimension(JCUnit.POINTS, 50, 50), new JCUnit.Measure (JCUnit.POINTS, 5));The preceding example produces the following result:
Figure 28 : A filled square with rounded corners.
6.9.4 Drawing Circles
To draw a circle, use the
frame.drawCircle(ds, new JCUnit.Point(JCUnit.POINTS, 175, 175), new JCUnit.Measure(JCUnit.POINTS, 25));JCFrame.drawCircle()
method. For instance, here is the code to place the center of the circle at the X- and Y-coordinates of 175 by 175 points, and to set the radius of the circle to 25 points:
JCUnit.Point
places the center of the circle at the X- and Y-coordinates of 175 by 175 points.JCUnit.Measure
sets the radius of the circle to 25 points.
Figure 29 : A circle with a radius of 25 points.
As with rectangles, you can draw circles filled with the color defined by the
frame.fillCircle(ds, new JCUnit.Point(JCUnit.POINTS, 225, 225), new JCUnit.Measure(JCUnit.POINTS, 25));JCDrawStyle
.The preceding example produces the following result:
Figure 30 : A filled circle with a radius of 25 points.
6.9.5 Drawing Polygons
JClass PageLayout also allows you to draw angular shapes with more than two sides - polygons. To draw a polygon, you create an
ArrayList
of points that define the X- and Y-coordinates of each of the polygon's corners. You then instruct JClass PageLayout to draw the polygon by connecting those points with lines.For example, you could use the following code to draw a triangle:
ArrayList list = new ArrayList();
list.add(new JCUnit.Point(JCUnit.POINTS, 275, 250));
list.add(new JCUnit.Point(JCUnit.POINTS, 300, 300));
list.add(new JCUnit.Point(JCUnit.POINTS, 250, 300));
frame.drawPolygon(ds, list);Using the X- and Y-coordinates as a guide, JClass PageLayout draws the following object:
Figure 31 : Constructing a polygon from a list of X- and Y-coordinates.
To draw more complex polygons, extend the list. For example, to draw a hexagon, define a total of six points in the list.
6.10 Render Objects
Render objects allow a description of the page-marking actions needed to draw a page stored in memory as the document is created. There are two kinds of render objects: those directly representing page-marking primitives (such as drawn lines or formatted text), and those representing additions to the flow of conceptual objects (such as horizontal rules). The current set of provided render objects includes all supported graphical primitives and all higher-level objects (for instance, images and tables) which can be added to the flow.
6.10.1 Render Object Categories
There are several interfaces that provide categorization of the types of render objects. The basic interfaces comprise:
- Embedable. Embedable objects can be added into the text flow as flowed objects because the interface provides the necessary support for determining the position of this object relative to the line contents. Text is not considered embedable because it defines the baseline of a line and doesn't need alignment attributes.
ImageRender
is the only Embedable render object.- Floatable. Floatable objects can be added into the document flow as independent paragraph-level objects that do not break lines and allow normal flowed content to be added while they are awaiting sufficient space.
ImageRender
is the only Floatable object.- FlowMarker. FlowMarker objects are not graphical primitives, but represent logical elements of the flow, which may have printer-specific or variable handling. The current FlowMarker objects are
HRuleMarker
,ImageMarker
, andTableMarker
.- Splitable. Splitable objects are flow objects that implement methods that allow them to be broken into smaller pieces in order to fit on lines. The best example is text, which can be divided into words or even individual letters.
StringRender
is currently the only Splitable object.
6.10.2 Subclasses of the Render object
The
com.klg.
jclass.page.render
package also contains classes that are not render objects; there are a number of page numbering and counting macros. The macro classes each implement a single page numbering or counting mechanism, such as representing the page number in roman numerals, and can be distinguished by names ending in...Macro.java
. The following table describes all objects that can be considered render objects.
6.11 Listening for JClass PageLayout Events
If your application needs to be informed about such events as the beginning, completion, or ending of a frame or a page, you can implement the
JCFlowListener
interface and examine the event to take appropriate action. TheJCFlowListener
implementation can either be passed to theJCFlow
constructor or added to an existingJCFlow
via theaddFlowListener()
method.JCFlowEvent
A
JCFlowEvent
occurs when a flow enters or exits a new frame or page as a result of document flow, and also when a frame or page is marked as complete by the resolution of embedded macros. The methods inJCFlowEvent
are:
JCFlowEvent Method
Description
The source of the event, the current
JCFlow
where the event occurred.JCFlowListener
JCFlowListener
methods each take aJCFlowEvent
as their only parameter.JCPrintEvent
A
JCPrintEvent
occurs when a document is opened or closed, or when a page begins or ends. The availability of aJCPrintEvent
overcomes a limitation of AWT printing. You can now be notified when a document finishes printing.
JCPrintEvent Method
Description
Returns the
eventId
. The returned value is one ofJCPrintEvent.BEGIN_PAGE
,JCPrintEvent.END_PAGE
,JCPrintEvent.OPEN_DOCUMENT
, orJCPrintEvent.CLOSE_DOCUMENT
.JCPrintListener
JCPrintListener
methods each take aJCPrintEvent
as their only parameter.